为sys.stdin设置较小的缓冲区大小?

时间:2010-09-08 17:25:14

标签: python stdin buffering

我正在使用以下bash命令模式运行memcached:

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

尝试跟踪平台范围内密钥的不匹配获取。

memtracer脚本位于下方并按预期工作,只有一个小问题。观察中间日志文件大小,memtracer.py直到memkeywatchYMD.log才开始输入 大小约为15-18K。是否有更好的方法来读取标准输入或者可能是将缓冲区大小降低到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], )

6 个答案:

答案 0 :(得分:31)

您可以使用python的-u标志从stdin / stdout中完全删除缓冲:

-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.

除此之外,不支持更改现有文件的缓冲,但是可以使用与现有文件相同的基础文件描述符创建新文件对象,并且可能使用不同的缓冲。 {3}}。即,

import os
import sys
newin = os.fdopen(sys.stdin.fileno(), 'r', 100)

newin绑定到文件对象的名称,该文件对象读取与标准输入相同的FD,但一次只缓冲大约100个字节(并且您可以继续{ {1}}从那里开始使用新文件对象作为标准输入。我说“应该”,因为这个区域使用在某些平台上存在许多错误和问题(提供跨平台的完全通用性非常困难) - 我不确定它是什么现在状态,但我肯定建议在所有感兴趣的平台上进行全面测试,以确保一切顺利。 (sys.stdin = newin,完全删除缓冲,如果可能满足您的要求,应该可以在所有平台上使用更少的问题。)

答案 1 :(得分:22)

您只需使用sys.stdin.readline()代替sys.stdin.__iter__()

import sys

while True:
    line = sys.stdin.readline()
    if not line: break # EOF

    sys.stdout.write('> ' + line.upper())

这使我在Ubuntu 13.04上使用Python 2.7.4和Python 3.3.1进行行缓冲读取。

答案 2 :(得分:10)

sys.stdin.__iter__仍然是行缓冲的,通过使用the 2-argument form of iter来构造迭代器,可以有一个迭代器,其行为大多相同(在EOF停止,而stdin.__iter__不会) sys.stdin.readline

import sys

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

或者提供None作为哨兵(但请注意,您需要自己处理EOF条件)。

答案 3 :(得分:3)

这在Python 3.4.3中适用于我:

procedure TFrmsearchpage.btnloadClick(Sender: TObject);
var
con:tfdconnection;
loadquery:tfdquery;
i:integer;
j:integer;
row:integer;
col1,col2,col3,col4, col5, col6, col7 : string;

begin
con:=tfdconnection.Create(nil);

loadquery:=tfdquery.Create(con);
loadquery.Connection:=con;
con.DriverName:='SQL';
con.Open('DriverID=SQLite;Database='+Dir+'/Stock_V5;');
loadquery.SQL.Text:='SELECT * FROM ' + edtdatabasename.Text;  //' Con Column';
 loadquery.Open;
  if loadquery.Eof then
    ShowMessage('not exists')
  else
    ShowMessage('exists');

for i := 0 to sgdproduct.RowCount do
    for j := 0 to sgdproduct.ColCount do
      sgdproduct.Cells[i,j]:='';
showmessage(loadquery.SQL.Text);
Sgdproduct.colcount:=7;
sgdproduct.fixedcols:=0;
for i := 0 to 3 do
sgdproduct.colwidths[i]:=100;
sgdproduct.cells[0,0] := 'Col1'; //?
sgdproduct.cells[1,0] := 'Col2';   //?
sgdproduct.cells[2,0] := 'Col3';
sgdproduct.cells[3,0] := 'Col4';  //?
sgdproduct.cells[4,0] := 'Col5';   //?
sgdproduct.cells[5,0] := 'Col6';  //?
sgdproduct.cells[6,0] := 'Col7';  //?
row:=1;
while not loadquery.Eof do
begin
    Col1:=query.FieldByName('Col1')//.As
  Col2:=query.FieldByName('Col2')//.As
  Col3:=query.FieldByName('Col3')//.As
  Col4:=query.FieldByName('Col4')//.As
  Col5:=query.FieldByName('Col5')//.As
 Col6:=query.FieldByName('Col6')//.As
 col7:=query.FieldByName('Col7')//.As
  sgdproduct.Cells[0,row]:=Col1;
   sgdproduct.Cells[1,row]:=Col2;
    sgdproduct.Cells[2,row]:=Col3;
     sgdproduct.Cells[3,row]:=Col4;
     sgdproduct.Cells[4,row]:=Col5;
          sgdproduct.Cells[5,row]:=Col6;
          sgdproduct.Cells[6,row]:=Col7;
      row:=row+1;
      query.Next;
end;
end;

documentation for fdopen()表示它只是import os import sys unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0) 的别名。

open()有一个可选的open()参数:

  

buffering 是一个可选的整数,用于设置缓冲策略。传递0以切换缓冲关闭(仅允许在二进制模式下),1选择行缓冲(仅在文本模式下可用),以及整数&gt; 1表示固定大小的块缓冲区的大小(以字节为单位)。

换句话说:

  • 完全无缓冲标准输入需要二进制模式,并将零作为缓冲区大小传递。
  • 线路缓冲需要文字模式。
  • 任何其他缓冲区大小似乎都适用于二进制文本模式(根据文档)。

答案 4 :(得分:1)

您的麻烦可能不在于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选项。

答案 5 :(得分:0)

我用python 2.7做的唯一方法是:

tty.setcbreak(sys.stdin.fileno())
来自Python nonblocking console input

。这样可以完全禁用缓冲并抑制回声。

编辑:关于亚历克斯的答案,在我的案例中,第一个命题(用-u调用python)是不可能的(见shebang limitation)。

当我使用0或1的缓冲区时,第二个命题(用较小的缓冲区复制fd:os.fdopen(sys.stdin.fileno(), 'r', 100))不起作用,因为它是用于交互式输入的,我需要立即处理每个按下的字符。