2010-09-08 32 views
21

Io corro memcached con il seguente modello di comando bash:Impostazione della dimensione del buffer più piccola per sys.stdin?

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

per cercare di rintracciare senza pari arriva a set di chiavi per la piattaforma larga.

Lo script di memtracer è sotto e funziona come desiderato, con un problema minore. Osservando la dimensione del file di registro intermedio, memtracer.py non inizia a ricevere input fino a quando memkeywatchYMD.log ha una dimensione di circa 15-18K. C'è un modo migliore per leggere in stdin o forse un modo per ridurre la dimensione del buffer fino a meno di 1k per tempi di risposta più rapidi?

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

risposta

26

È possibile rimuovere completamente il buffer da stdin/stdout utilizzando -u bandiera del pitone:

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

e la pagina man chiarisce:

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

Al di là di questo, alterando il buffer per un il file esistente non è supportato, ma tu puoi creare un nuovo oggetto file con lo stesso descrittore di file sottostante s uno esistente e possibilmente diverso buffering, usando os.fdopen. Vale a dire,

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

dovrebbe legano newin al nome di un oggetto file che legge lo stesso FD come input standard, ma tamponata da solo circa 100 byte alla volta (e si potrebbe continuare con sys.stdin = newin a utilizzare il nuovo oggetto file come input standard da lì in poi). Dico "dovrebbe" perché questa area utilizzava per avere un certo numero di bug e problemi su alcune piattaforme (è una funzionalità piuttosto difficile da fornire a tutta la piattaforma con generalità completa) - Non sono sicuro di quale sia il suo stato ora, ma Raccomando senz'altro test approfonditi su tutte le piattaforme di interesse per garantire che tutto vada liscio. (-u, rimuovere completamente il buffer, dovrebbe funzionare con meno problemi su tutte le piattaforme, se questo potrebbe soddisfare i requisiti).

+0

grazie, il flag -u per un ambiente Linux è stato il vincitore. In precedenza avevo provato ad utilizzare os.fdopen e ho avuto lo stesso problema di buffering, anche se impostassi la dimensione del buffer su 10. – David

+5

Sfortunatamente, Python 3 ancora apre ostinato 'stdin' in modalità testo bufferizzata. Solo 'stdout' e' stderr' sono influenzati dall'opzione '-u' ora. –

+0

Qualche soluzione per Python3? Forse una libreria/opzione guidata dagli eventi? –

18

Si può semplicemente utilizzare sys.stdin.readline() invece di sys.stdin.__iter__():

import sys 

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

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

Questo mi dà line-buffered legge utilizzando Python 2.7.4 e Python 3.3.1 su Ubuntu 13.04.

+2

Questo non è veramente rilevante per la domanda, intendevi renderlo un commento. – David

+2

Come ho capito, la domanda era "C'è un modo migliore per leggere in stdin" [per evitare problemi di buffer di input quando si utilizza uno script Python in una pipeline], e la mia risposta (a tre anni di ritardo) è "Sì , usa 'readline' invece di' __iter__' ". Ma forse la mia risposta dipende dalla piattaforma, e hai ancora problemi con il buffer se provi il codice sopra? –

+0

Ahkey, ho capito. Intendevo MOLTO dimensioni del buffer più piccole (come 80 byte o meno) per il buffering dello stdin. Per 2.7 non è possibile effettuare tali dimensioni del buffer senza la bandiera -U che Alex menziona nella sua risposta. – David

7

Il sys.stdin.__iter__ rimanendo linea tamponata, si può avere un iteratore che si comporta principalmente identico (fermate EOF, che non si stdin.__iter__) utilizzando the 2-argument form of iter per fare un iteratore di sys.stdin.readline:

import sys 

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

Or fornire None come sentinella (ma si noti che quindi è necessario gestire la condizione EOF da soli).

+1

Sembra che sarebbe stato meglio come commento alla risposta di Soren. Alex Martelli e Soren hanno fornito delle risposte, mentre questo è un ulteriore miglioramento del contributo di Soren. – David

+0

Quello che proponi qui qui è la soluzione migliore che ho visto per questo problema orribile; Sto per scorrere tutto il mio codice Python e sostituire "per riga in sys.stdin" con esso. Vedo che è in realtà elencato nella pagina di riferimento a cui ti sei riferito. Quello che non mi è ancora chiaro è ... perché mai "per linea in sys.stdin" si comportano in modo diverso da "per line in iter (sys.stdin.readline, ''):"? Per quanto posso vedere sono semanticamente identici, tranne che il comportamento della versione precedente è quello che mi sembra un insetto cattivo, un comportamento che nessuno potrebbe mai desiderare. Se qualcuno ha un controesempio, mi piacerebbe vederlo. –

+0

@DonHatch durante l'iterazione su stdin Sono d'accordo sul fatto che il comportamento sia strano e simile a un bug, ma quando il file non stdin la lettura di 8k contemporaneamente migliorerà le prestazioni. –

2

questo ha lavorato per me in Python 3.4.3:

import os 
import sys 

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

Il documentation for fdopen() dice che è solo un alias per open().

open() ha un buffering parametro opzionale:

buffer è un intero opzionale utilizzato per impostare la politica di buffering. Passa 0 per disattivare il buffering (consentito solo in modalità binaria), 1 per selezionare il buffering di riga (utilizzabile solo in modalità testo) e un numero intero> 1 per indicare la dimensione in byte di un buffer di blocco a dimensione fissa.

In altre parole:

  • completamente senza buffer stdin richiede modalità binaria e passando zero come la dimensione del buffer.
  • Il buffer di linea richiede la modalità testo.
  • Qualsiasi altra dimensione del buffer sembra funzionare in entrambe le modalità binarie e text (in base alla documentazione).
0

L'unico modo ho potuto farlo con python 2.7 era:

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

da Python nonblocking console input. Questo disabilita completamente il buffering e sopprime anche l'eco.

MODIFICA: Per quanto riguarda la risposta di Alex, la prima proposizione (invocazione di python con -u) non è possibile nel mio caso (vedere shebang limitation).

La seconda proposizione (duplicazione di fd con buffer più piccolo: os.fdopen(sys.stdin.fileno(), 'r', 100)) non funziona quando utilizzo un buffer di 0 o 1, come per un input interattivo e ho bisogno che ogni carattere premuto venga elaborato immediatamente.

+0

Strano, la risposta di Alex ha funzionato per me allora. Mi chiedo se un aggiornamento del backport ha cambiato/rotto qualcosa – David

Problemi correlati