2016-02-15 37 views
5

Al fine di cercare di capire core.async, ho provato senza successo ad attuare la "Skynet 1 milione microbenchmark", che è:Come implementare il microbenchmark Skynet 1m con core.async?

Crea un attore (goroutine, qualunque cosa), che genera 10 nuovi attori, ognuna di esse genera altri 10 attori, ecc. fino a quando un milione di attori non sono stati creati a livello . Quindi, ognuno di essi restituisce il numero ordinale (da 0 a 999999), che vengono sommati al livello precedente e restituiti a monte, fino a raggiungere l'utente root. (La risposta dovrebbe essere 499999500000).

ci sono applicazione in molte lingue qui:

https://github.com/atemerev/skynet

Ecco il mio tentativo totalmente rotto:

(defn skynet [chan num size div] 
    (if (= 1 size) 
    (>! chan num) 
    (>! chan (reduce + (let [rc (async/chan) 
          n (/ size div)] 
          (doall (for [i [0 div]] 
            (skynet rc (+ num (* i n)) n div)) 
           (for [i [0 div]] (<! rc)))))))) 

E stavo cercando di chiamare il tutto dall'interno di un blocco di andare a la REPL:

(time (go (<!! (skynet (async/chan) 0 1000000 10)))) 

Probabilmente sono seriamente confuso su molte cose riguardanti core.async (e anche la valutazione lazy).

Come devo risolvere questo problema e perché?

risposta

7

Ci sono some limitations su ciò che core.async è in grado di fare, quindi non è possibile utilizzare le funzioni map o for.

L'implementazione è molto simile a quella corretta. Alcuni punti:

  1. go == un processo, così si sono solo la creazione di un processo, non 1m
  2. <!! deve essere utilizzato al di fuori andare bloccare
  3. <! deve essere utilizzato all'interno di andare blocchi
  4. si utilizza in modo non corretto for
  5. doall accetta un solo parametro

Un'implementazione di lavoro che, probabilmente, può essere migliorato:

(defn skynet [parent num size div] 
    (go ;; We create a new process each time skynet is called 
    (if (= 1 size) 
     (>! parent num) 
     (let [self (chan) 
      new-size (/ size div)] 
     (dotimes [i div] ;; dotimes is more explicit for side effects 
      (skynet self (+ num (* i new-size)) new-size div)) 
    (loop [i div ;; Manual reduce 
      t 0] 
     (if (zero? i) 
     (>! parent t) 
     (recur (dec i) 
       (+ t (<! self))))))))) 

E chiamarlo:

(time 
    (do 
    (def result (chan)) 
    (def x (skynet result 0 1000000 10)) 
    (<!! result))) 
+0

Grazie mille! Un sacco di Clojure da studiare ma questo sicuramente mi aiuterà molto, non solo la tua versione ma anche i tuoi commenti su cosa c'è di sbagliato nel mio codice. Stranamente alcuni * "a /" * sono comparsi nell'ultima riga di codice, l'ho modificato :) –

+0

Naturalmente più lento del semplice uso di un riduttore, ma questo è il punto del benchmark. Ho provato a scrivere una versione della funzione 'skynet' che restituirebbe un canale contenente il risultato (invece di prenderne uno come argomento), e usare async/ridurre invece di una riduzione manuale e non riuscire a trovare un modo per farlo. Sarei interessato a una tale versione. Comunque, Complimenti! – nha