2011-02-16 6 views

Respuesta

18

Si marca la fuente que ver:

> (use 'clojure.repl) 
> (source pmap) 
(defn pmap 
    "Like map, except f is applied in parallel. Semi-lazy in that the 
    parallel computation stays ahead of the consumption, but doesn't 
    realize the entire result unless required. Only useful for 
    computationally intensive functions where the time of f dominates 
    the coordination overhead." 
    {:added "1.0"} 
    ([f coll] 
    (let [n (+ 2 (.. Runtime getRuntime availableProcessors)) 
     rets (map #(future (f %)) coll) 
     step (fn step [[x & xs :as vs] fs] 
       (lazy-seq 
       (if-let [s (seq fs)] 
        (cons (deref x) (step xs (rest s))) 
        (map deref vs))))] 
    (step rets (drop n rets)))) 
    ([f coll & colls] 
    (let [step (fn step [cs] 
       (lazy-seq 
       (let [ss (map seq cs)] 
        (when (every? identity ss) 
        (cons (map first ss) (step (map rest ss)))))))] 
    (pmap #(apply f %) (step (cons coll colls)))))) 

El (+ 2 (.. Runtime getRuntime availableProcessors)) es una gran pista allí. pmap tomará las primeras piezas de trabajo (+ 2 processors) y las ejecutará de forma asíncrona a través del future. Entonces, si tienes 2 núcleos, lanzará 4 piezas de trabajo a la vez, tratando de mantenerte un poco adelantado, pero el máximo debería ser 2 + n.

future en última instancia, utiliza el agente de grupo de subprocesos de E/S que soporta un número ilimitado de hilos. Crecerá a medida que se lanza trabajo y se reducirá si los hilos no se usan.

+1

Así es la respuesta corta que 'pmap' está perfectamente bien para el envío de una gran cantidad de llamadas web y procesar las respuestas? ¿Hay alguna advertencia? – dan

+4

Puedo estar equivocado, pero el problema probablemente sea que los n + 2 hilos bloquearán la espera de las respuestas web. Por lo tanto, no obtendrá suficientes solicitudes de vuelo para obtener el máximo rendimiento: pmap está diseñado para cargas de trabajo vinculadas a la CPU. Si esto le sucede a usted, entonces puede simplemente ajustar cada llamada de solicitud en un futuro y todos volarán a la vez. – mikera

+4

Bueno, nunca hay una respuesta corta con concurrencia. :) Diría que pmap en realidad no es ideal para este caso de uso. Realmente desea esperar * todas * las fuentes en paralelo - pmap retrasará el inicio de la quinta en el caso anterior.A MENOS QUE, no necesariamente quiera pasar por todas sus fuentes, en cuyo caso el comportamiento perezoso de pmap es bueno. Me sentiría tentado de que tus cosas se localicen en las fuentes y utilicen el futuro para realizar cada solicitud. –

9

Sobre la base de una excelente respuesta de Alex que explica cómo funciona la PMAP, aquí está mi sugerencia para su situación:

(doall 
    (map 
    #(future (my-web-fetch-function %)) 
    list-of-xml-feeds-to-fetch)) 

Justificación:

  • que desea como muchas piezas de trabajo durante el vuelo como puedas , ya que la mayoría se bloqueará en la red IO.
  • futura disparar una pieza asíncrono de trabajo para cada solicitud, que deberá tratarse en un grupo de subprocesos. Puedes dejar que Clojure se encargue de eso inteligentemente.
  • El Doall en el mapa obligará a la evaluación de la secuencia completa (es decir, el lanzamiento de todas las solicitudes).
  • Su hilo principal puede iniciar la eliminación de referencias a los futuros de inmediato, y por lo tanto puede seguir avanzando ya que los resultados individuales regresan
+0

Creo que los futuros usan un grupo de subprocesos sin límites, por lo que ejecutar esto en una gran colección de feeds podría causar problemas. – Glen

+1

Además, es probable que desee volver a trazar el mapa para 'deref' el futuro para que sepa cuándo se ha completado todo. – Joe

3

No hay tiempo para escribir una larga respuesta, pero hay una clojure.contrib http-agente que crea cada solicitud get/post como su propio agente. Así que puedes disparar miles de solicitudes y todas se ejecutarán en paralelo y se completarán a medida que aparezcan los resultados.

0

Mirando el funcionamiento de pmap, parece ir 32 hilos a la vez sin importar la cantidad de procesadores que tener, el problema es que el mapa se adelantará al cálculo por 32 y los futuros se inician por sí mismos. (Muestra) (defn samplef [n] (println "starting " n) (Thread/sleep 10000) n) (def result (pmap samplef (range 0 100)))

; esperará 10 segundos y verá 32 copias, luego cuando tome la 33 y otra 32 ; imprime este minuto que está haciendo 32 hilos simultáneos a la vez ; para mí esto no es perfecto ; SALUDOS Felipe

Cuestiones relacionadas