Установка меньшего размера буфера для sys.stdin? - PullRequest
23 голосов
/ 08 сентября 2010

Я использую memcached со следующей комбинацией команд bash:

memcached -vv 2>&1 | tee memkeywatch2010098.log 2>&1 | ~/bin/memtracer.py | tee memkeywatchCounts20100908.log

, чтобы попытаться отследить непревзойденный доступ к наборам для ключей всей платформы.

Ниже приведен скрипт memtracerработает по желанию, с одной незначительной проблемой.Наблюдая за размером промежуточного файла журнала, memtracer.py не начинает получать ввод, пока размер файла memkeywatchYMD.log не достигнет 15-18K.Есть ли лучший способ чтения в stdin или, возможно, способ сократить размер буфера до 1 КБ для более быстрого времени отклика?

#!/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], )

Ответы [ 5 ]

32 голосов
/ 08 сентября 2010

Вы можете полностью удалить буферизацию из stdin / stdout, используя -u флаг python:

-u     : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
         see man page for details on internal buffering relating to '-u'

, и страница руководства уточняет:

   -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, чтобы использовать новый файловый объект в качестве стандартного ввода и далее).Я говорю «должен», потому что эта область использовала , чтобы иметь ряд ошибок и проблем на некоторых платформах (довольно сложно обеспечить кроссплатформенность с полной общностью) - я не уверен, каково ее состояниесейчас, но я бы определенно рекомендовал провести тщательное тестирование на всех интересующих платформах, чтобы убедиться, что все идет гладко.(-u, полностью удаляя буферизацию, должно работать с меньшим количеством проблем на всех платформах, если это может удовлетворить ваши требования).

22 голосов
/ 14 августа 2013

Вы можете просто использовать 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.

10 голосов
/ 07 марта 2015

sys.stdin.__iter__, все еще будучи буферизованным строкой, можно иметь итератор, который ведет себя в основном одинаково (останавливается на EOF, тогда как stdin.__iter__ не будет), используя форму с двумя аргументами iter для создания итератора sys.stdin.readline:

import sys

for line in iter(sys.stdin.readline, ''):
    sys.stdout.write('> ' + line.upper())

или укажите None в качестве сторожа (но учтите, что тогда вам нужно обработать условие EOF самостоятельно).

3 голосов
/ 07 декабря 2015

Это работает для меня в Python 3.4.3:

import os
import sys

unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)

Документация для fdopen() говорит, что это просто псевдоним для open().

open() имеет необязательный параметр buffering:

buffering - необязательное целое число, используемое для установки политики буферизации.Пропустите 0, чтобы выключить буферизацию (разрешено только в двоичном режиме), 1, чтобы выбрать буферизацию строки (можно использовать только в текстовом режиме), и целое число> 1, чтобы указать размер в байтах буфера фрагмента фиксированного размера.

Другими словами:

  • Полностью небуферизованный stdin требует двоичного режима и передачи нуля в качестве размера буфера.
  • Строковая буферизация требует текст режим.
  • Кажется, что любой другой размер буфера работает как в двоичном , так и тексте режимов (согласно документации).
0 голосов
/ 03 февраля 2017

Единственный способ, которым я мог сделать это с Python 2.7, был:

tty.setcbreak(sys.stdin.fileno())

из Python неблокирующий консольный ввод . Это полностью отключает буферизацию, а также подавляет эхо.

РЕДАКТИРОВАТЬ: Что касается ответа Алекса, первое предложение (вызов Python с -u) в моем случае не представляется возможным (см. ограничение Шебанга ).

Второе предложение (дублирование fd с меньшим буфером: os.fdopen(sys.stdin.fileno(), 'r', 100)) не работает, когда я использую буфер 0 или 1, поскольку это для интерактивного ввода, и мне нужно, чтобы каждый символ был нажат для немедленной обработки.

...