2016-05-25 8 views
1

Ahoi. Ich wurde beauftragt, die Leistung von Bit.ly's Data_Hacks' sample.py als Übung zu verbessern.Iterieren über eine Datei ohne Linien auf der Grundlage der Bedingung effizient

Ich habe einen Teil des Codes zythonisiert. und enthielt einen PCG-Zufallsgenerator, der die Leistung bisher um etwa 20 Sekunden (im Vergleich zu 72s) verbessert und die Druckausgabe optimiert hat (durch Verwendung einer Basis-C-Funktion anstelle von Pythons write()).

Das hat alles gut funktioniert, aber abgesehen von diesen Verbesserungen, würde ich gerne den Loop selbst optimieren.

Die Grundfunktion, wie in sample.py der bit.ly gesehen:

def run(sample_rate): 
    input_stream = sys.stdin 
    for line in input_stream: 
     if random.randint(1,100) <= sample_rate: 
      sys.stdout.write(line) 

Meine Implementierung:

cdef int take_sample(float sample_rate): 
    cdef unsigned int floor = 1 
    cdef unsigned int top = 100 
    if pcg32_random() % 100 <= sample_rate: 
     return 1 
    else: 
     return 0 


def run(float sample_rate, file): 
    cdef char* line 
    with open(file, 'rb') as f: 
     for line in f: 
      if take_sample(sample_rate): 
       out(line) 

Was ich jetzt verbessern möchte, speziell die nächste Zeile übersprungen wird (und vorzugsweise tun Sie dies wiederholt), wenn mein take_sample() nicht True zurückgibt.

Meine aktuelle Implementierung ist dies:

def run(float sample_rate, file): 
    cdef char* line 

    with open(file, 'rb') as f: 

     for line in f: 
      out(line) 
      while not take_sample(sample_rate): 
       next(f) 

, die nichts zu tun, scheinen die Leistung zu verbessern - was ich mich zu vermuten, habe lediglich einen continue Ruf nach einer, wenn der Bedingung an der Spitze der Schleife ersetzt, mit meine next(f). diese

Die Frage ist also:

Gibt es eine effizientere Art und Weise in einer Schleife über eine Datei (in Cython)?

Ich möchte Linien ganz wegzulassen, nur dann wirklich, wenn ich mein nennen out() zugegriffen werden sollte sie bedeutet - ist dies bereits der Fall in for Schleife des Python? Ist line ein Zeiger (oder vergleichbar mit) auf die Zeile der Datei? Oder lädt die Schleife das tatsächlich?

Ich weiß, dass ich es verbessern könnte, indem ich es komplett in C schreibe, aber ich würde gerne wissen, wie weit ich es schaffen kann, mit python/cython weiterzumachen.

Update: Ich habe eine C-Variante meines Codes - mit dem gleichen Testfall - getestet und es bei unter 2s (überraschend niemand). Obwohl es richtig ist, dass der Zufallsgenerator und die Datei-E/A im Allgemeinen zwei große Flaschenhälse sind, sollte darauf hingewiesen werden, dass die Dateiverarbeitung von python an sich schon langsam ist.

Also, gibt es eine Möglichkeit, die Datei lesen von C zu verwenden, außer die Schleife selbst in Cython implementieren? Der Overhead verlangsamt den Python-Code immer noch deutlich, was mich dazu bringt, mich zu fragen, ob ich einfach an der Schallmauer der Performance stehe, wenn es um die Dateiverwaltung mit Cython geht?

+1

Diese Beschreibung könnte helfen: http://rabexc.org/posts/io-performance-in-python –

+0

Engpässe sind Datei-IO und die Zufallsfunktion. Das ursprüngliche Skript hat also kein wesentliches Optimierungspotenzial. – Daniel

+1

Jede Iteration auf 'f' liest eine Zeile. Eine Zeile ist definiert als die Zeichenfolge von Bytes bis zum nächsten '\ nl'. Die einzige Möglichkeit, die der Leser findet, ist das Lesen der Bytes nacheinander. Für einen Text gibt es keinen Index für Zeilenanfänge, den er zum "Suchen" verwenden kann. Es kann also nicht zur nächsten Zeile springen, ohne den aktuellen zu lesen. – hpaulj

Antwort

0

Wenn die Datei klein ist, können Sie sie ganz mit gleichzeitig lesen (möglicherweise reduziert IO-Verkehr) und die Reihenfolge der Zeilen iterieren. Wenn die Abtastrate klein genug ist, können Sie eine Stichprobenentnahme aus einer geometrischen Verteilung in Erwägung ziehen, die möglicherweise effizienter ist.

Ich weiß nicht, cython, aber ich würde beachten Sie auch:

  • take_sample() durch Entfernen von unnötigen Variablen vereinfachen und boolean Ergebnis des Tests Rückkehr statt integer,
  • Änderung Unterschrift von take_sample() zu take_sample(int) zu Vermeide die Int-zu-Float-Konvertierung bei jedem Test.

[EDIT]

Nach Kommentar von @hpaulj, kann es besser sein, wenn Sie .read().split('\n') statt .readlines() von mir vorgeschlagen verwenden.

+0

Die Dateien sind leider ziemlich groß; Der Testfall hat etwa 90 Millionen Textzeilen. – nlsdfnbch

+1

@ j4ck Scheint, dass Sie Dateipufferung für den gleichen Zweck verwenden können: http://stackoverflow.com/questions/29712445/what-is-the-use-of-buffering-in-pythons-built-in-open-function – abukaj