Sempre fiquei surpreso / frustrado com o tempo que leva para simplesmente enviar para o terminal com uma declaração de impressão. Após um registro lento e doloroso recente, decidi investigar e fiquei surpreso ao descobrir que quase todo o tempo gasto está aguardando o terminal processar os resultados.
A escrita no stdout pode ser acelerada de alguma forma?
Eu escrevi um script (' print_timer.py
' na parte inferior desta pergunta) para comparar o tempo ao escrever 100 mil linhas no stdout, no arquivo e com o stdout redirecionado para/dev/null
. Aqui está o resultado do tempo:
$ python print_timer.py
this is a test
this is a test
<snipped 99997 lines>
this is a test
-----
timing summary (100k lines each)
-----
print :11.950 s
write to file (+ fsync) : 0.122 s
print with stdout = /dev/null : 0.050 s
Uau. Para garantir que o python não esteja fazendo algo nos bastidores, como reconhecer que eu redesignei o stdout para / dev / null ou algo assim, fiz o redirecionamento fora do script ...
$ python print_timer.py > /dev/null
-----
timing summary (100k lines each)
-----
print : 0.053 s
write to file (+fsync) : 0.108 s
print with stdout = /dev/null : 0.045 s
Portanto, não é um truque de python, é apenas o terminal. Eu sempre soube que descarregar a saída para / dev / null acelerava as coisas, mas nunca imaginei que fosse tão significativo!
Me surpreende o quão lento o tty é. Como pode ser que gravar no disco físico é MUITO mais rápido do que gravar na "tela" (presumivelmente uma operação totalmente com RAM) e é tão rápido quanto simplesmente despejar no lixo com / dev / null?
Este link fala sobre como o terminal bloqueará a E / S para que ele possa "analisar [a entrada], atualizar seu buffer de quadros, se comunicar com o servidor X para rolar a janela e assim por diante" ... mas eu não entendi completamente. O que pode demorar tanto?
Espero que não haja saída (com exceção de uma implementação tty mais rápida?), Mas acho que perguntaria assim mesmo.
ATUALIZAÇÃO: depois de ler alguns comentários, me perguntei quanto impacto meu tamanho de tela realmente teria no tempo de impressão e isso tem algum significado. Os números realmente lentos acima estão com o meu terminal Gnome ampliado para 1920x1200. Se reduzi-lo muito pequeno, fico ...
-----
timing summary (100k lines each)
-----
print : 2.920 s
write to file (+fsync) : 0.121 s
print with stdout = /dev/null : 0.048 s
Isso é certamente melhor (~ 4x), mas não muda minha pergunta. Isso só aumenta a minha pergunta, pois não entendo por que a renderização da tela do terminal deve diminuir a velocidade de um aplicativo gravando no stdout. Por que meu programa precisa aguardar a renderização da tela para continuar?
Todos os aplicativos terminal / tty não são criados iguais? Eu ainda tenho que experimentar. Parece-me realmente que um terminal deve ser capaz de armazenar em buffer todos os dados recebidos, analisá-los / renderizá-los invisivelmente e renderizar apenas o bloco mais recente que é visível na configuração atual da tela a uma taxa de quadros sensata. Portanto, se eu puder gravar + fsync no disco em ~ 0,1 segundos, um terminal poderá concluir a mesma operação em algo dessa ordem (com talvez algumas atualizações na tela enquanto o fazia).
Eu ainda espero que exista uma configuração tty que possa ser alterada do lado do aplicativo para melhorar esse comportamento para o programador. Se isso for estritamente um problema de aplicativo de terminal, talvez isso nem pertença ao StackOverflow?
o que estou perdendo?
Aqui está o programa python usado para gerar o tempo:
import time, sys, tty
import os
lineCount = 100000
line = "this is a test"
summary = ""
cmd = "print"
startTime_s = time.time()
for x in range(lineCount):
print line
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
#Add a newline to match line outputs above...
line += "\n"
cmd = "write to file (+fsync)"
fp = file("out.txt", "w")
startTime_s = time.time()
for x in range(lineCount):
fp.write(line)
os.fsync(fp.fileno())
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
cmd = "print with stdout = /dev/null"
sys.stdout = file(os.devnull, "w")
startTime_s = time.time()
for x in range(lineCount):
fp.write(line)
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
print >> sys.stderr, "-----"
print >> sys.stderr, "timing summary (100k lines each)"
print >> sys.stderr, "-----"
print >> sys.stderr, summary