2015-06-01 5 views
7

Betrachten Sie das folgende Beispiel minimal:cython boundscheck = True schneller als boundscheck = False

#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True 
cimport cython 
from libc.stdlib cimport malloc 

def main(size_t ni, size_t nt, size_t nx): 
    cdef: 
     size_t i, j, t, x, y 
     double[:, :, ::1] a = <double[:ni, :ni, :nx]>malloc(ni * ni * nx * sizeof(double)) 
     double[:, :, ::1] b = <double[:nt, :ni, :nx]>malloc(nt * ni * nx * sizeof(double)) 
     size_t[:, :, ::1] best = <size_t[:nt, :ni, :nx]>malloc(nt * ni * nx * sizeof(size_t)) 
     size_t mxi 
     double s, mxs 
    for t in range(nt): 
     for j in range(ni): 
      for y in range(nx): # this loops does nothing but is needed for the effect below. 
       mxs = -1e300 
       for i in range(ni): 
        for x in range(nx): 
         with cython.boundscheck(False): # Faster!?!? 
          s = b[t, i, x] + a[i, j, x] 
         if s >= mxs: 
          mxs = s 
          mxi = i 
       best[t + 1, j, y] = mxi 
    return best[0, 0, 0] 

im Wesentlichen zwei entlang einiger spezifischen Achsen 2D-Arrays Summieren und den maximier Index entlang einer anderen Achse zu finden.

Wenn mit gcc -O3 kompiliert und mit den Argumenten (1, 2000, 2000) aufgerufen wird, führt das Hinzufügen von boundscheck = True zu einer doppelt schnelleren Ausführung als bei boundscheck = False.

Irgendein Hinweis, warum das der Fall wäre? (Nun, ich kann wahrscheinlich vermuten, dass dies wieder mit der Autokorrektur des GCC zu tun hat ...)

Vielen Dank im Voraus.

(Cross-Gepostet von cython-Benutzer)

+1

In meinen Tests, die Version mit 'mit cython.boundscheck (True)' ist etwa 3 mal langsamer. Ich denke, die Erinnerung an 'a',' b', 'best' ist wegen' malloc' nicht initialisiert. Ich änderte das zu einem äquivalenten Call-Call. Auch scheint die Zeile 'best [t + 1, j, y]' ungültigen Speicher zu indizieren, wenn 't == nt - 1'. –

+0

Das Ändern von mallocs in Callocs und Ersetzen von "t + 1" durch "t" ändert (qualitativ) die Ergebnisse für mich nicht. Mein 'setup.py' hat' extra_compile_args = ["- O3"] 'und ich benutze gcc 5.1.0. – antony

+1

OK, ich habe es mit -O3 versucht, und ich bekomme die gleiche Geschwindigkeit mit beiden Versionen. Ich benutze gcc 4.9.1. Wenn ich mir den generierten C-Code anschaue, denke ich, dass gcc intelligent genug ist, um zu wissen, dass die Extra-Grenzen-Checks nie ausgelöst werden (wegen der Bedingung für die Schleife). Ich weiß nicht, warum Sie so unterschiedliche Geschwindigkeiten bekommen. –

Antwort

-1

Boundscheck ist eine Sicherheitsüberprüfung, die Sie Indizes innerhalb der Grenzen der Vektoren zugreifen. Wenn Sie sich nicht darum kümmern, ob die Indizes aus dem Rahmen gehen können, ist es schneller. Es braucht Zeit, um die Überprüfung durchzuführen.

Das heißt, wenn boundcheck wahr ist, wird überprüft, ob der Index innerhalb des Bereichs des Vektors liegt, bevor er in den Speicher gelesen oder geschrieben wird. Und wenn nicht, wird es einen Fehler werfen. Wenn boundcheck falsch ist, liest oder schreibt es in den Zeiger, selbst wenn der Index außerhalb der Grenzen liegt, wobei falsche Daten ausgegeben werden, indem Daten durch Schreiben und Schreiben in den Speicher korrumpiert werden.

Aus Dokumentation:

Die Array-Lookups werden noch immer von zwei Faktoren gebremst:

1) Bounds Prüfung durchgeführt wird.

2) Negative Indizes werden geprüft und korrekt behandelt.

Die Folgen der nicht gebundenen Kontrolle Wesen:

Jetzt begrenze Überprüfung nicht durchgeführt wird (und als Nebeneffekt, wenn Sie ‚‘ Sie ‚‘ geschehen außerhalb der Grenzen für den Zugriff werden Sie im besten Fall crash dein Programm und im schlimmsten Fall korrupte Daten).

Wo dies besonders wichtig ist, können Sie keine Vektoren haben. Hier ist die Warnung von der Dokumentation:

Warnung

Geschwindigkeit mit einigen Kosten verbunden. Insbesondere kann es gefährlich sein, typisierte Objekte (wie f, g und h in unserem Beispielcode) auf None zu setzen. Einstellung solche Objekte zu None ist völlig legal, aber alles, was Sie mit ihnen tun können ist, ob sie keine sind. Jegliche andere Verwendung (Attributsuche oder Indizierung) kann möglicherweise Daten segregieren oder korrumpieren (anstatt Ausnahmen wie in Python auszulösen). Die tatsächlichen Regeln sind ein bisschen komplizierter, aber die Hauptmeldung lautet clear: Verwenden Sie keine typisierten Objekte, ohne zu wissen, dass sie nicht auf None gesetzt sind.

http://docs.cython.org/src/userguide/numpy_tutorial.html

+2

Ich denke, der Grund, warum die Frage gestellt wurde, ist, dass in diesem Fall 'boundscheck (True)' schneller ist, was kontraintuitiv ist. – DavidW

+0

Ohh !!!!! Guter Punkt! Es ist so kontra intuitiv, dass ich sogar die Frage falsch gelesen habe! Ich dachte, die Frage stelle das Gegenteil. –