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

Dav*_*vid 24 python stdin buffering

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

memcached -vv 2>&1 | tee memkeywatch2010098.log 2>&1 | ~/bin/memtracer.py | tee memkeywatchCounts20100908.log
Run Code Online (Sandbox Code Playgroud)

尝试跟踪无与伦比的获取到平台键的集合.

memtracer脚本位于下方并按预期工作,只有一个小问题.看到中间日志文件大小,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], )
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 33

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

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

并且手册页澄清了:

   -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.
Run Code Online (Sandbox Code Playgroud)

除此之外,不支持更改现有文件的缓冲,但是您可以使用os.fdopen创建具有与现有文件相同的基础文件描述符的新文件对象,并可能使用不同的缓冲.也就是说,

import os
import sys
newin = os.fdopen(sys.stdin.fileno(), 'r', 100)
Run Code Online (Sandbox Code Playgroud)

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

  • 不幸的是,Python 3仍然顽固地在缓冲文本模式下打开`stdin`.现在只有`stdout`和`stderr`受到`-u`开关的影响. (9认同)
  • 这在Python 3.4.3中适用于我:`os.fdopen(sys.stdin.fileno(),'rb',buffering = 0)` (3认同)
  • @DenilsonSáMaia:无需自己重新打开它。`sys.stdin` 实际上是三层;一个“io.TextIOWrapper”(将“bytes”解码为“str”),包装一个“io.BufferedReader”(缓冲“bytes”),包装一个“io.FileIO”(提交系统调用的实际内容)。它们都可以作为属性使用;`sys.stdin.buffer` 获取没有文本解码的 `BufferedReader`,`sys.stding.buffer.raw` 获取没有缓冲的 `FileIO`。 (3认同)

Sør*_*org 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())
Run Code Online (Sandbox Code Playgroud)

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

  • 这与问题无关,你的意思是将其作为评论. (2认同)
  • 我的理解,问题是"是否有更好的方法标准输入读" [在管道中使用Python脚本时,为了避免输入缓冲器问题]我的回答(三年制晚,因为它可能是)回答"是,使用`readline`而不是`__iter__`".但也许我的答案是依赖于平台的,如果你尝试上面的代码,你仍然有缓冲问题? (2认同)
  • 有趣的亚历克斯没有抓住这个,https://github.com/certik/python-2.7/blob/c360290c3c9e55fbd79d6ceacdfc7cd4f393c1eb/Objects/fileobject.c#L1377你说得对,读取线可能更快,因为它在file_internext缓冲区中逐步使用getc 8192如源中所定义. (2认同)

Ant*_*ala 11

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())
Run Code Online (Sandbox Code Playgroud)

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

  • 作为对soren的回答的评论,这似乎会更好。Alex Martelli和Soren提供了答案,但更多的是Soren输入的改进。 (2认同)

Den*_*aia 6

这在 Python 3.4.3 中对我有用:

import os
import sys

unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)
Run Code Online (Sandbox Code Playgroud)

的文档fdopen()说它只是 的别名open()

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

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

换句话说:

  • 完全无缓冲的标准输入需要二进制文件模式并传递零作为缓冲区大小。
  • 行缓冲需要文本模式。
  • 任何其他缓冲区大小似乎都可以在二进制文本模式下工作(根据文档)。