2012-05-14 20 views
7

Estoy tratando de entender los futuros de Clojure, y he visto ejemplos de los libros comunes de Clojure, y hay ejemplos donde los futuros se usan para cálculos paralelos (lo que parece tener sentido).entendiendo Clojure futuros

Sin embargo, espero que alguien pueda explicar el comportamiento de un ejemplo simple adaptado del libro Programación Clojure de O'Reilly.

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

cuando trato de eliminar la referencia de este, haciendo

(time @long-calculation) 

Se devuelve el resultado correcto (4999999950000000), pero casi al instante (en 0.045 milisegundos) en mi máquina.

Pero cuando llamo a la función real, al igual que

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

puedo obtener el resultado correcto también, pero el tiempo empleado es mucho más grande (~ 5000 milisegundos).

Cuando elimino el futuro, entiendo que se crea un nuevo hilo en el que se evalúa la expresión, en cuyo caso también esperaría que tardara unos 5000 mseg.

¿Cómo es que el futuro desreferenciado devuelve el resultado correcto tan rápido?

Respuesta

11

El cálculo en un futuro comienza tan pronto como crea el futuro (en una secuencia separada). En su caso, el cálculo se inicia tan pronto como se ejecute (def long-calculation ....)

dereferencing hará una de dos cosas:

  • Si el futuro no se ha completado, el bloque hasta que se complete y luego devuelve el valor (esto podría tome una cantidad de tiempo arbitraria, o incluso nunca complete si el futuro no finaliza)
  • Si el futuro se ha completado, devuelva el resultado. Esto es casi instantánea (por lo que usted está viendo retornos dereference muy rápido)

se puede ver el efecto mediante la comparación de los siguientes:

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

¿Hay una desventaja de utilizar un gran número de futuros? He estado escribiendo un código que hace cálculos numéricamente intensivos en varios lugares. En lugar de utilizar matrices nativas de Java o hacer insinuaciones de tipo, ¿puedo simplemente escribir el código funcional idiomático y 'futuro' los resultados de estos cálculos en su lugar? – endbegin

+2

Los futuros son bastante livianos pero tienen algunos gastos generales, así que evitaría usarlos para cálculos extremadamente pequeños. Si desea hacer cálculos en paralelo, considere usar 'pmap', que es una versión concurrente de' map' que usa futuros bajo el capó. Habiendo dicho eso, si su código realmente es numéricamente intensivo, probablemente sea mejor utilizar ambas matrices Java * y * pmap/futuros si quiere sacar lo mejor de su tiempo de CPU. – mikera

+0

He intentado jugar con pmap, pero lo he encontrado útil solo cuando el tamaño de los datos es "grande" (qué tan grande es algo subjetivo, por supuesto). He estado aprendiendo Clojure mediante la implementación de algunas funciones simples de procesamiento de señales digitales, y hay una notable ventaja de velocidad con el uso de matrices Java nativas sobre un estilo funcional con la abstracción seq en el modo de procesador único/hilo. Si utilizo futuros, la aceleración es tan inmensa que realmente no importa si uso un código nativo o funcional. Siente que me falta algo obvio. – endbegin