Por que a impressão em stdout é tão lenta? Pode ser acelerado?

Sempre fiquei impressionado / frustrado com o tempo que leva para simplesmente enviar para o terminal com uma declaração de impressão. Depois de um registro recente e dolorosamente lento, decidi investigar e fiquei surpreso ao descobrir que quasetudo 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 em stdout, em arquivo e com 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 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?

Esse link fala sobre como o terminal bloqueará a E / S para que possa"analise [a entrada], atualize seu buffer de quadro, comunique-se com o servidor X para rolar a janela e assim por diante"... mas eu não entendo completamente. O que pode demorar tanto?

Espero que não haja saída (exceto por uma implementação tty mais rápida?), Mas acho que perguntaria assim mesmo.

ATUALIZAÇÃO: depois de ler alguns comentários, perguntei-me quanto impacto o tamanho da minha 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 eu 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. Apenasacrescenta para minha pergunta, pois não entendo por que a renderização da tela do terminal deve diminuir a velocidade de um aplicativo gravando para 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).

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

questionAnswers(6)

yourAnswerToTheQuestion