2010-10-19 2 views
9

Es gibt dieses Skript namens svnmerge.py, das ich versuche, ein bisschen zu optimieren und zu optimieren. Ich bin jedoch völlig neu in Python, also ist es nicht einfach.Wie optimiert man Operationen für große (75.000 Elemente) Sätze von Booleans in Python?

Das aktuelle Problem scheint im Skript auf eine Klasse namens RevisionSet bezogen zu sein. Im Wesentlichen erstellt es eine große Hashtabelle (?) Von booleschen Werten mit ganzzahligen Schlüsseln. Im schlimmsten Fall - eine für jede Revision in unserem SVN-Repository, die jetzt fast 75.000 ist.

Danach führt es Set-Operationen auf solchen großen Arrays - Addition, Subtraktion, Schnittpunkt und so weiter. Die Implementierung ist die einfachste O (n) -Implementierung, die bei solchen großen Mengen natürlich ziemlich langsam wird. Die gesamte Datenstruktur könnte optimiert werden, da lange Bereiche kontinuierlicher Werte vorhanden sind. Beispielsweise können alle Schlüssel von 1 bis 74.000 true enthalten. Auch das Skript ist für Python 2.2 geschrieben, was eine ziemlich alte Version ist und wir benutzen 2.6 sowieso, also könnte es dort auch etwas zu gewinnen geben.

Ich könnte versuchen, dies zusammen zu schaffen, aber es wäre schwierig und würde viel Zeit in Anspruch nehmen - ganz zu schweigen davon, dass es irgendwo schon implementiert sein könnte. Obwohl ich die Lernerfahrung gerne hätte, ist das Ergebnis jetzt wichtiger. Was würdest du mir vorschlagen?

+0

Welche Operationen möchten Sie in der Booleschen Liste durchführen? Würde Ihnen eine Reihe von Booleans helfen? – eumiro

+0

Diese Set-Implementierung sieht aus wie O (n), nicht O (n * m). 'if r in rs', wo' rs' ein dict ist, ist eine O (1) -Operation, nicht O (len (rs)). –

+0

@Baffe Boyois - wahr, kommen Sie, um darüber nachzudenken. Der Fragetext wurde korrigiert. –

Antwort

7

Sie könnten versuchen, es mit numpy anstelle von einfachen Python zu tun. Ich fand es sehr schnell für Operationen wie diese.

Zum Beispiel:

# Create 1000000 numbers between 0 and 1000, takes 21ms 
x = numpy.random.randint(0, 1000, 1000000) 

# Get all items that are larger than 500, takes 2.58ms 
y = x > 500 

# Add 10 to those items, takes 26.1ms 
x[y] += 10 

Da das mit viel mehr Zeilen ist, glaube ich, dass 75000 sollte kein Problem sein, entweder :)

+0

OK, ich überprüfe es. Ich werde deine Antwort akzeptieren, wenn ich sie benutze. –

+0

Persönlich glaube ich nicht, dass hier numpy wirklich gefragt ist. Die eingebauten Sätze von Python reichen dafür völlig aus, denke ich. –

+0

Sie könnten wahrscheinlich numpy auch 8-Bit-Ganzzahlen verwenden, wenn Sie Ihren Speicherbedarf reduzieren möchten. Ich bin mir jedoch nicht sicher, ob Sie das mit der Randint-Funktion machen können. http://docs.scipy.org/doc/numpy/user/basics.types.html – GWW

0

Zum Beispiel können alle Tasten von 1 bis 74.000 enthalten true

Warum nicht an einer Untergruppe arbeiten? Nur 74001 bis zum Ende.

Beschneiden 74/75 Ihrer Daten ist viel einfacher als zu versuchen, einen Algorithmus schlauer zu schreiben als O (n).

+0

Natürlich, aber dann müsste ich das ganze Drehbuch neu schreiben. –

+0

@ Vilx: Wie so? Sie müssen nur Dinge unterteilen. –

+0

Ich glaube, Sie haben mich vielleicht falsch verstanden. Das sind keine echten Zahlen, es ist nur etwas, das ich auf der Stelle erfunden habe. Ich will nur sagen, dass es große Intervalle desselben booleschen Werts gibt. –

0

Sie sollten RevisionSet neu schreiben, um eine Reihe von Revisionen zu haben. Ich denke, die interne Darstellung für eine Revision sollte eine Ganzzahl sein und Revisionsbereiche sollten nach Bedarf erstellt werden.

Es gibt keinen zwingenden Grund, Code zu verwenden, der Python 2.3 und früher unterstützt.

0

Nur ein Gedanke. Ich habe so etwas mit Run-Coding in der binären Bildbearbeitung gemacht. Speichern Sie jedes Set als eine Reihe von Zahlen: Anzahl der Bits aus, Anzahl der Bits an, Anzahl der Bits aus usw.

Dann können Sie alle Arten von Booleschen Operationen auf ihnen als Dekorationen auf einer einfachen Zusammenführung tun Algorithmus.

1

Hier ist ein schneller Ersatz für RevisionSet, die es zu einem Satz macht. Es sollte viel schneller sein. Ich habe es nicht vollständig getestet, aber es hat mit allen Tests funktioniert, die ich gemacht habe. Es gibt zweifellos andere Möglichkeiten, die Dinge zu beschleunigen, aber ich denke, dass dies wirklich helfen wird, weil es die schnelle Implementierung von Mengen nutzt, anstatt Schleifen in Python zu machen, die der ursprüngliche Code in Funktionen wie __sub__ und __and__ tat. Das einzige Problem ist, dass der Iterator nicht sortiert ist.Möglicherweise müssen Sie ein wenig Code ändern, um dies zu berücksichtigen. Ich bin sicher, es gibt andere Möglichkeiten, dies zu verbessern, aber hoffentlich wird es Ihnen einen guten Start geben.

class RevisionSet(set): 
    """ 
    A set of revisions, held in dictionary form for easy manipulation. If we 
    were to rewrite this script for Python 2.3+, we would subclass this from 
    set (or UserSet). As this class does not include branch 
    information, it's assumed that one instance will be used per 
    branch. 
    """ 
    def __init__(self, parm): 
     """Constructs a RevisionSet from a string in property form, or from 
     a dictionary whose keys are the revisions. Raises ValueError if the 
     input string is invalid.""" 


     revision_range_split_re = re.compile('[-:]') 

     if isinstance(parm, set): 
      print "1" 
      self.update(parm.copy()) 
     elif isinstance(parm, list): 
      self.update(set(parm)) 
     else: 
      parm = parm.strip() 
      if parm: 
       for R in parm.split(","): 
        rev_or_revs = re.split(revision_range_split_re, R) 
        if len(rev_or_revs) == 1: 
         self.add(int(rev_or_revs[0])) 
        elif len(rev_or_revs) == 2: 
         self.update(set(range(int(rev_or_revs[0]), 
             int(rev_or_revs[1])+1))) 
        else: 
         raise ValueError, 'Ill formatted revision range: ' + R 

    def sorted(self): 
     return sorted(self) 

    def normalized(self): 
     """Returns a normalized version of the revision set, which is an 
     ordered list of couples (start,end), with the minimum number of 
     intervals.""" 
     revnums = sorted(self) 
     revnums.reverse() 
     ret = [] 
     while revnums: 
      s = e = revnums.pop() 
      while revnums and revnums[-1] in (e, e+1): 
       e = revnums.pop() 
      ret.append((s, e)) 
     return ret 

    def __str__(self): 
     """Convert the revision set to a string, using its normalized form.""" 
     L = [] 
     for s,e in self.normalized(): 
      if s == e: 
       L.append(str(s)) 
      else: 
       L.append(str(s) + "-" + str(e)) 
     return ",".join(L) 

Zusatz: By the way, ich verglichenen Gewerkschaften, Kreuzungen und Subtraktionen des ursprünglichen RevisionSet und meinen RevisionSet oben zu tun, und der obige Code ist aus 3x für diese Vorgänge schneller 7x, wenn sie auf zwei Betrieb RevisionSets mit 75000 Elementen. Ich weiß, dass andere Leute sagen, dass numpy der richtige Weg ist, aber wenn du mit Python nicht sehr erfahren bist, wie dein Kommentar zeigt, dann möchtest du diesen Weg vielleicht nicht gehen, weil er viel mehr Änderungen beinhalten wird. Ich würde empfehlen, meinen Code zu testen, um zu sehen, ob er funktioniert, und wenn ja, dann sehen Sie, ob er schnell genug für Sie ist. Wenn dies nicht der Fall ist, würde ich versuchen, ein Profil zu erstellen, um zu sehen, was verbessert werden muss. Nur dann würde ich in Betracht ziehen, numpy zu verwenden (was ein sehr gutes Paket ist, das ich ziemlich häufig benutze).

+0

'def sortiert (self): return sortiert (self)' - das scheint mir unheilvoll ... –

+0

@Vilx, können Sie das entfernen, wenn Sie einfach die gleichen 3 Stellen in der Datei ersetzen, wo die sortierte Methode mit nur sortiert aufgerufen wird (theRevSet) –

+0

Es ist nicht rekursive Stack-Überlauf dann? Ich habe heute mit dem Python-Tutorial angefangen, bin aber noch nicht zum Unterricht gekommen. : P –

Verwandte Themen