2012-05-14 11 views
7

Ich versuche Clojure-Futures zu verstehen, und ich habe Beispiele aus den gängigen Clojure-Büchern gesehen, und es gibt Beispiele, in denen Futures für parallele Berechnungen verwendet werden (was sinnvoll erscheint).Clojure-Futures verstehen

Ich hoffe jedoch, dass jemand das Verhalten eines einfachen Beispiels erklären kann, das aus O'Reillys Programmier-Clojure-Buch stammt.

(def long-calculation (future (apply + (range 1e8)))) 

Wenn ich versuche, dies zu dereferenzieren, von

tun
(time @long-calculation) 

Es gibt das richtige Ergebnis (4999999950000000), aber fast sofort (in 0.045 msec) auf meinem Rechner.

Aber wenn ich die eigentliche Funktion aufrufen, wie so

(time (apply + (range 1e8))) 

bekomme ich das auch richtige Ergebnis, aber die benötigte Zeit ist viel größer (~ 5000 msec).

Wenn ich die Zukunft dereferenziere, ist mein Verständnis, dass ein neuer Thread erstellt wird, auf dem der Ausdruck ausgewertet wird - in diesem Fall würde ich erwarten, dass es auch etwa 5000 ms dauert.

Wie kommt die dereferenzierte Zukunft so schnell zum richtigen Ergebnis?

Antwort

11

Die Berechnung in einer Zukunft beginnt, sobald Sie die Zukunft erstellen (in einem separaten Thread). In Ihrem Fall, beginnt die Berechnung, sobald Sie (def long-calculation ....)

Dereferenzieren eines von zwei Dingen tun ausführen:

  • Wenn die Zukunft noch nicht abgeschlossen hat, Block, bis er abgeschlossen ist und dann wieder den Wert (dies könnte nehmen Sie eine willkürliche Menge von Zeit, oder beenden Sie nie, wenn die Zukunft nicht beendet wird)
  • Wenn die Zukunft abgeschlossen ist, geben Sie das Ergebnis zurück. Dies ist fast augenblicklich (weshalb Sie sehr schnell dereferenzieren kehrt zu sehen)

Sie den Effekt durch den Vergleich der folgenden sehen:

;; dereference before future completes 
(let [f (future (Thread/sleep 1000))] 
    (time @f)) 
=> "Elapsed time: 999.46176 msecs" 

;; dereference after future completes 
(let [f (future (Thread/sleep 1000))] 
    (Thread/sleep 2000) 
    (time @f)) 
=> "Elapsed time: 0.039598 msecs" 
+0

Gibt es ein Nachteil, eine große Anzahl von Futures zu verwenden? Ich habe einen Code geschrieben, der an mehreren Stellen numerisch intensive Berechnungen durchführt. Anstatt native Java-Arrays zu verwenden oder Hinting zu schreiben, kann ich stattdessen idiomatischen funktionalen Code und 'future' die Ergebnisse dieser Berechnungen schreiben? – endbegin

+2

Futures sind ziemlich leicht, haben aber etwas Overhead, so dass ich sie für extrem kleine Berechnungen vermeiden würde. Wenn Sie Berechnungen parallel durchführen möchten, sollten Sie 'pmap' verwenden - eine gleichzeitige Version von' map', die Futures unter der Haube verwendet. Wenn Ihr Code wirklich numerisch intensiv ist, verwenden Sie wahrscheinlich am besten beide Java-Arrays * und * pmap/futures, wenn Sie das Beste aus Ihrer CPU-Zeit herausholen wollen. – mikera

+0

Ich habe versucht, mit pmap zu spielen, aber habe es nur nützlich gefunden, wenn die Datengröße "groß" ist (wie groß ist natürlich etwas subjektiv). Ich habe Clojure gelernt, indem ich einige einfache digitale Signalverarbeitungsfunktionen implementiert habe, und es gibt einen spürbaren Geschwindigkeitsvorteil bei der Verwendung von nativen Java-Arrays gegenüber einem funktionalen Stil mit der seq-Abstraktion im Einzelprozessor/Thread-Modus. Wenn ich Futures verwende, ist die Beschleunigung so immens, dass es wirklich keine Rolle spielt, ob ich nativen oder funktionalen Code verwende. Fühlt sich an, als würde ich etwas Offensichtliches vermissen. – endbegin