Установка меньшего размера буфера для sys.stdin?
Я запускаю memcached со следующим шаблоном команды bash:
memcached -vv 2>&1 | tee memkeywatch2010098.log 2>&1 | ~/bin/memtracer.py | tee memkeywatchCounts20100908.log
чтобы попытаться отследить непревзойденный доступ к наборам для широкой платформы ключей.
Memtracer script ниже и работает по желанию с одной незначительной проблемой. Наблюдая за размером промежуточного файла журнала, memtracer.py не начинает получать ввод, пока memkeywatchYMD.log
составляет около 15-18K. Есть ли лучший способ прочитать в stdin или, возможно, способ сократить размер буфера до менее 1k для более быстрого времени отклика?
#!/usr/bin/python
import sys
from collections import defaultdict
if __name__ == "__main__":
keys = defaultdict(int)
GET = 1
SET = 2
CLIENT = 1
SERVER = 2
#if <
for line in sys.stdin:
key = None
components = line.strip().split(" ")
#newConn = components[0][1:3]
direction = CLIENT if components[0].startswith("<") else SERVER
#if lastConn != newConn:
# lastConn = newConn
if direction == CLIENT:
command = SET if components[1] == "set" else GET
key = components[2]
if command == SET:
keys[key] -= 1
elif direction == SERVER:
command = components[1]
if command == "sending":
key = components[3]
keys[key] += 1
if key != None:
print "%s:%s" % ( key, keys[key], )
Ответы
Ответ 1
Вы можете полностью удалить буферизацию из stdin/stdout с помощью флага python -u
:
-u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
see man page for details on internal buffering relating to '-u'
и поясняет man-страницу:
-u Force stdin, stdout and stderr to be totally unbuffered. On
systems where it matters, also put stdin, stdout and stderr in
binary mode. Note that there is internal buffering in xread-
lines(), readlines() and file-object iterators ("for line in
sys.stdin") which is not influenced by this option. To work
around this, you will want to use "sys.stdin.readline()" inside
a "while 1:" loop.
Помимо этого, изменение буферизации для существующего файла не поддерживается, но вы можете создать новый файловый объект с тем же базовым файловым дескриптором, что и существующий, и, возможно, с другой буферизацией, используя os.fdopen. То есть.
import os
import sys
newin = os.fdopen(sys.stdin.fileno(), 'r', 100)
должен привязать newin
к имени файлового объекта, который считывает тот же FD, что и стандартный ввод, но буферизуется только по 100 байт за раз (и вы можете продолжить с sys.stdin = newin
для использования нового файлового объекта как стандартный ввод оттуда). Я говорю "должен", потому что в этой области было несколько ошибок и проблем на некоторых платформах (это довольно сложная функциональность, обеспечивающая кросс-платформу с полной общностью). Я не уверен, каково ее состояние сейчас, d обязательно рекомендую провести тщательное тестирование на всех платформах, чтобы гарантировать, что все идет гладко. (-u
, полностью удаляя буферизацию, должно работать с меньшим количеством проблем на всех платформах, если это может соответствовать вашим требованиям).
Ответ 2
Вы можете просто использовать sys.stdin.readline()
вместо sys.stdin.__iter__()
:
import sys
while True:
line = sys.stdin.readline()
if not line: break # EOF
sys.stdout.write('> ' + line.upper())
Это дает мне чтение в виде строки с использованием Python 2.7.4 и Python 3.3.1 на Ubuntu 13.04.
Ответ 3
sys.stdin.__iter__
, все еще буферизируемый по строке, может иметь итератор, который ведет себя в основном идентично (останавливается при EOF, тогда как stdin.__iter__
не будет), используя 2- аргумент iter
, чтобы сделать итератор sys.stdin.readline
:
import sys
for line in iter(sys.stdin.readline, ''):
sys.stdout.write('> ' + line.upper())
Или укажите None
как часовую (но учтите, что тогда вам нужно самостоятельно обрабатывать условие EOF).
Ответ 4
Это работало для меня в Python 3.4.3:
import os
import sys
unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
Документация для fdopen()
говорит, что это просто псевдоним для open()
.
open()
имеет необязательный параметр buffering
:
Буферизация - необязательное целое число, используемое для установки политики буферизации. Передайте 0, чтобы отключить буферизацию (разрешено только в двоичном режиме), 1 выбрать буферизацию строки (только для использования в текстовом режиме) и целое число > 1, чтобы указать размер в байтах буфера блоков фиксированного размера.
Другими словами:
- Полностью небуферизованный stdin требует двоичного режима и пропускает нуль в качестве размера буфера.
- Линейная буферизация требует текста.
- Любой другой размер буфера, похоже, работает как в двоичном, так и в текстовом режимах (согласно документации).
Ответ 5
Единственный способ сделать это с помощью python 2.7:
tty.setcbreak(sys.stdin.fileno())
from Неблокирующий консольный вход Python. Это полностью отключает буферизацию, а также подавляет эхо.
EDIT: Что касается ответа Alex, первое предложение (вызов python с -u
) в моем случае невозможно (см. ограничение shebang).
Второе предложение (дублирование fd с меньшим буфером: os.fdopen(sys.stdin.fileno(), 'r', 100)
) не работает, когда я использую буфер 0 или 1, как и для интерактивного ввода, и мне нужно, чтобы каждый символ был нажат для немедленной обработки.
Ответ 6
Возможно, ваши проблемы связаны не с Python, а с буферизацией, которую вводит оболочка Linux при объединении команд в каналы. В этом случае входные данные буферизуются не строкой, а блоком 4K.
Чтобы остановить эту буферизацию, перед цепочкой команд введите команду unbuffer
из пакета expect
, например:
unbuffer memcached -vv 2>&1 | unbuffer -p tee memkeywatch2010098.log 2>&1 | unbuffer -p ~/bin/memtracer.py | tee memkeywatchCounts20100908.log
Команде unbuffer
требуется опция -p
при использовании в середине конвейера.