Я всегда удивлялся/разочаровался в том, сколько времени требуется, чтобы просто вывести на терминал оператор печати. После некоторых недавних болезненно медленных лесозаготовок я решил изучить это и был очень удивлен, обнаружив, что почти все потраченное время ждет, пока терминал обработает результаты.Почему печать на stdout так медленно? Можно ли его ускорить?
Может ли запись на stdout ускориться?
Я написал сценарий ('print_timer.py
' в нижней части этого вопроса), чтобы сравнить время при записи строк 100k в stdout, в файл и с stdout, перенаправленным на /dev/null
. Вот результат синхронизации:
$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
Ничего себе. Для того, чтобы убедиться, что питон не делать что-то за кулисами, как признание того, что я переназначить стандартный вывод в/DEV/нуль или что-то, что я сделал перенаправление вне сценария ...
$ 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
Так что не питон трюк, это просто терминал. Я всегда знал, что демпинг выводится на/dev/null, но никогда не думал, что это так важно!
Меня поражает, насколько медленным является tty. Как может случиться так, что запись на физический диск происходит быстрее, чем запись на «экран» (предположительно, все оперативной памяти), и эффективно так же быстро, как просто сбрасывать мусор с помощью/dev/null?
This link говорит о том, как терминал будет блокировать I/O, чтобы он мог «разобрать [вход], обновить свой буфер кадров, обмен данными с сервером X для того, чтобы прокрутить окно и так далее» ... но я не полностью понял это. Что может занять так много времени?
Я ожидаю, что нет выхода (за исключением более быстрой реализации tty?), Но в любом случае я бы спросил.
UPDATE: после прочтения некоторых комментариев я задавался вопрос, сколько влияния моего размера экрана на самом деле имеет на время печати, и это имеет какое-то значение. Очень медленные цифры выше - мой терминал Gnome взлетел до 1920x1200. Если я уменьшаю его очень мелко, я получаю ...
----- timing summary (100k lines each) ----- print : 2.920 s write to file (+fsync) : 0.121 s print with stdout = /dev/null : 0.048 s
Это, безусловно, лучше (~ 4x), но не меняет мой вопрос. Это только добавляет к моему вопросу, поскольку я не понимаю, почему рендеринг экрана терминала замедляет приложение, записывающее в stdout. Почему моя программа должна ждать продолжения экрана?
Все ли приложения терминала/tty не созданы равными? Мне еще предстоит экспериментировать. Мне кажется, что терминал должен иметь возможность буферизовать все входящие данные, анализировать/визуализировать его невидимо и отображать только последний фрагмент, который отображается в текущей конфигурации экрана с разумной частотой кадров. Поэтому, если я могу записать + fsync на диск через ~ 0,1 секунды, терминал должен иметь возможность выполнить одну и ту же операцию в чем-то из этого порядка (возможно, с несколькими обновлениями экрана, когда он это сделал).
Я все еще надеюсь, что есть установка tty, которая может быть изменена со стороны приложения, чтобы сделать это поведение лучше для программиста. Если это строго проблема терминального приложения, возможно, это даже не относится к StackOverflow?
Что мне не хватает?
Вот программа питона используется для генерации времени:
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
Вся цель написания на stdout - так человек может прочитать результат. Ни один человек в мире не может читать 10 000 строк текста за 12 секунд, так что в чем смысл ускорения работы stdout? –
@Seun Osewa: Один пример (который вызвал мой вопрос) - это когда вы делаете такие вещи, как [отладка отписки печати] (http://stackoverflow.com/questions/189562). Вы хотите запустить свою программу и увидеть результаты по мере их возникновения. Вы, очевидно, правы, что большинство линий будут летать по тому, что вы не видите, но когда происходит исключение (или вы попадаете в условный оператор getch/raw_input/sleep, который вы тщательно размещаете), вы хотите смотреть прямо на печать, а не на постоянно открывать или обновлять вид файла. – Russ
Отладка утверждения печати является одной из причин, по которым устройства tty (т.е. терминалы) по умолчанию используют буферизацию строк вместо буферизации блоков: вывод отладки не очень полезен, если программа зависает, а последние несколько строк вывода отладки все еще находятся в вместо сброса на терминал. –