Я всегда удивлялся/расстраивался тем, сколько времени требуется, чтобы просто вывести на терминал оператор печати. После недавнего болезненного медленного ведения журнала я решил изучить его и был очень удивлен, обнаружив, что почти все потраченное время ждет, пока терминал обработает результаты.
Может ли запись на stdout ускоряться?
Я написал script ('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
Ого. Чтобы убедиться, что python не делает что-то за кулисами, например, признавая, что я переназначил stdout на /dev/null или что-то еще, я сделал перенаправление вне 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
Итак, это не трюк python, это просто терминал. Я всегда знал, что вывод данных на /dev/null ускоряется, но никогда не думал, что это важно!
Меня поражает, насколько медленным является tty. Как может случиться так, что запись на физический диск происходит быстрее, чем запись на "экран" (предположительно, все оперативной памяти), и эффективно так же быстро, как просто сбрасывать мусор с помощью /dev/null?
Эта ссылка рассказывает о том, как терминал будет блокировать ввод-вывод, чтобы он мог "анализировать [ввод], обновлять свой буфер кадров, общаться с сервером X, чтобы прокручивать окно и т.д."... но я не полностью его понимаю. Что может занять так много времени?
Я ожидаю, что нет выхода (за исключением более быстрой реализации tty?), но в любом случае я бы спросил.
ОБНОВЛЕНИЕ: после прочтения некоторых комментариев я задавался вопросом, насколько сильно влияет размер экрана на время печати, и это имеет некоторое значение. Очень медленные цифры выше - мой терминал 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. Почему моя программа должна ждать продолжения рендеринга экрана?
Не созданы ли все приложения terminal/tty равными? Мне еще предстоит экспериментировать. Мне кажется, что терминал должен иметь возможность буферизовать все входящие данные, анализировать/визуализировать его невидимо и отображать только последний фрагмент, который отображается в текущей конфигурации экрана с разумной частотой кадров. Поэтому, если я могу записать + fsync на диск через ~ 0,1 секунды, терминал должен иметь возможность выполнить одну и ту же операцию в чем-то из этого порядка (возможно, с несколькими обновлениями экрана, когда он это сделал).
Я все еще надеюсь, что есть параметр tty, который можно изменить со стороны приложения, чтобы сделать это поведение лучше для программиста. Если это строго проблема с терминальным приложением, то это, возможно, даже не относится к StackOverflow?
Что мне не хватает?
Вот программа python, используемая для генерации синхронизации:
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