2010-09-08 6 views
21

Ich laufe mit den folgenden Bash-Befehlsmustern Memcached:Einstellung der kleineren Puffergröße für sys.stdin?

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

, um zu versuchen und die Spur unerreicht bekommt breit für Schlüssel-Plattform setzt.

Das Memtracer-Skript ist unten und funktioniert wie gewünscht, mit einem kleinen Problem. Wenn Sie die Größe der Zwischenprotokolldatei beobachten, beginnt memtracer.py erst mit der Eingabe, bis die Datei memkeywatchYMD.log ungefähr 15-18 KB groß ist. Gibt es eine bessere Möglichkeit, stdin zu lesen oder eine Möglichkeit, die Puffergröße für schnellere Antwortzeiten auf unter 1k zu reduzieren?

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

Antwort

26

Sie vollständig Pufferung von stdin entfernen/stdout unter Verwendung -u Flagge Python:

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

und die man-Seite stellt klar:

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

Darüber hinaus ist für eine den Puffer verändern vorhandene Datei wird nicht unterstützt, aber Sie können ein neues Dateiobjekt mit dem gleichen zugrunde liegenden Dateideskriptor erstellen s eine bestehende und möglicherweise unterschiedliche Pufferung mit os.fdopen. Dh,

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

sollte binden newin auf den Namen einer Datei-Objekt, das die gleiche FD als Standardeingabe liest, aber nur um etwa 100 Bytes zu einer Zeit gepuffert (und man konnte mit sys.stdin = newin weiterhin die verwenden neues Dateiobjekt als Standardeingabe von dort an). Ich sage "sollte", weil dieser Bereich verwendet, um eine Reihe von Fehlern und Problemen auf einigen Plattformen zu haben (es ist ziemlich schwer Funktionalität plattformübergreifend mit voller Allgemeinheit zu bieten) - Ich bin mir nicht sicher, was sein Zustand ist, aber Ich würde definitiv empfehlen, auf allen relevanten Plattformen gründlich zu testen, um sicherzustellen, dass alles reibungslos abläuft. (-u, die Pufferung vollständig zu entfernen, sollte mit weniger Problemen auf allen Plattformen funktionieren, wenn dies Ihren Anforderungen entspricht).

+0

danke, die -u-Flagge für eine Linux-Umgebung war der Gewinner. Ich hatte vorher os.fdopen ausprobiert und lief auf dasselbe Pufferungsproblem, selbst wenn ich die Puffergröße auf 10 setze. – David

+5

Unglücklicherweise öffnet Python 3 hartnäckig 'stdin' im gepufferten Textmodus. Nur "stdout" und "stderr" sind jetzt von dem Schalter "-u" betroffen. –

+0

Irgendwelche Work-arounds für Python3? Vielleicht eine ereignisgesteuerte Bibliothek/Option? –

18

Sie können einfach sys.stdin.readline() anstelle von sys.stdin.__iter__():

import sys 

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

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

Das gibt mir Line-gepufferte liest mit Python 2.7.4 und Python 3.3.1 auf Ubuntu 13.04.

+2

Dies ist nicht wirklich relevant für die Frage, meinst du, dies als Kommentar zu machen. – David

+2

Wie ich verstanden habe, war die Frage "Gibt es eine bessere Möglichkeit zum lesen in Stdin" [um Eingabepufferprobleme zu vermeiden, wenn ein Python-Skript in einer Pipeline], und meine Antwort (drei Jahre zu spät) ist "Ja , benutze 'readline' anstelle von' __iter__' ". Aber vielleicht ist meine Antwort plattformabhängig, und Sie haben immer noch Pufferprobleme, wenn Sie den obigen Code ausprobieren? –

+0

Ahkey, ich verstehe. Ich meinte VIEL kleinere Puffergrößen (wie 80 Bytes oder weniger) für Stdin-Pufferung. Für 2.7 kann man diese Puffergrößen nicht ohne die -U-Flagge, die Alex in seiner Antwort erwähnt, bewirken. – David

7

Die sys.stdin.__iter__ noch wobei linien gepuffert, kann man einen Iterator, die meist gleich verhält (stoppt bei EOF, während stdin.__iter__ nicht) durch the 2-argument form of iter mit einem Iterator sys.stdin.readline zu machen:

import sys 

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

Or Stellen Sie None als den Sentinel zur Verfügung (aber beachten Sie, dass Sie dann die EOF-Bedingung selbst behandeln müssen).

+1

Dies scheint als wäre es besser gewesen, Sorens Antwort zu kommentieren. Alex Martelli und Soren haben Antworten gegeben, während dies mehr eine Verbesserung für Sorens Beitrag ist. – David

+0

Was Sie hier vorschlagen, ist die beste Lösung, die ich für dieses schreckliche Problem gesehen habe; Ich bin dabei, meinen ganzen Python-Code durchzusuchen und "für Zeile in sys.stdin" damit zu ersetzen. Ich sehe, dass es tatsächlich in der Ref-Seite aufgelistet ist, auf die Sie sich bezogen haben. Was mir noch nicht klar ist, ist ... warum in aller Welt verhält sich "für Zeile in sys.stdin" anders als "für Zeile in iter (sys.stdin.readline, ''):"? Soweit ich sehen kann, sind sie semantisch identisch, außer dass das Verhalten der früheren Version mir wie ein hässlicher Fehler erscheint, Verhalten, das niemand jemals wollen könnte. Wenn jemand ein Gegenbeispiel hat, würde ich es gerne sehen. –

+0

@DonHatch beim Iterieren von stdin Ich stimme zu, dass das Verhalten seltsam und fehlerartig ist, aber wenn die Datei nicht stdin ist, wird das Lesen von 8k auf einmal die Leistung verbessern. –

2

Dieser arbeitete für mich in Python 3.4.3:

import os 
import sys 

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

Die documentation for fdopen() sagt es nur ein Alias ​​für open() ist.

open() hat einen optionalen Parameter buffering:

Pufferung eine optionale ganze Zahl ist, verwendet, um die Pufferung Politik einzustellen. Übergeben Sie 0, um die Pufferung auszuschalten (nur im Binärmodus zulässig), 1, um die Zeilenpufferung auszuwählen (nur im Textmodus verwendbar), und eine Ganzzahl> 1, um die Größe eines Pufferpuffers mit fester Größe in Byte anzugeben.

Mit anderen Worten:

  • Fully ungepufferte stdin erfordert binären Modus und Null als die Puffergröße übergeben.
  • Zeilenpufferung erfordert Text Modus.
  • Jede andere Puffergröße scheint in beiden Binär und Text Modi zu funktionieren (gemäß der Dokumentation).
0

Die einzige Art, wie ich es mit Python tun konnte 2.7 war:

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

von Python nonblocking console input. Dadurch wird die Pufferung komplett deaktiviert und das Echo unterdrückt.

BEARBEITEN: In Bezug auf Alex Antwort ist der erste Vorschlag (Python mit -u aufrufen) in meinem Fall nicht möglich (siehe shebang limitation).

Der zweite Vorschlag (duplizieren von fd mit kleineren Puffer: os.fdopen(sys.stdin.fileno(), 'r', 100)) funktioniert nicht, wenn ich einen Puffer von 0 oder 1, wie es für eine interaktive Eingabe ist, und ich brauche jedes Zeichen gedrückt, um sofort verarbeitet werden.

+0

Seltsam, Alex Antwort funktionierte für mich damals. Ich frage mich, ob ein Backport-Update etwas verändert/kaputt gemacht hat – David