Loops inside of a (go ...) are much slower. Only affects cljs, not clj

194 views
Skip to first unread message

Marcus Lewis

unread,
Jan 10, 2015, 5:32:31 AM1/10/15
to clojur...@googlegroups.com
I'm doing some <canvas /> work and I was having some real trouble getting it performant.

The cause: I was looping over every pixel inside a `(go ...)`. This appears to sabotage `loop` and `doseq` perf, but only in ClojureScript. Numbers below.

These numbers are taken with:
    [org.clojure/clojurescript "0.0-2511"]
    [org.clojure/core.async "0.1.346.0-17112a-alpha"]
though I also saw this issue with [org.clojure/clojurescript "0.0-2644"].

It gets dramatically worse as you increase the pixel count. With 4 million pixels the increase is 6ms => 2265ms, pure overhead. If you move the loop into a helper function, the issue goes away.

ClojureScript:

(time 
  (loop [i 0]
  (when (< i 40000)
  (recur (inc i)))))
;  "Elapsed time: 1 msecs"

(go
  (time
    (loop [i 0]
  (when (< i 40000)
  (recur (inc i))))))
;  "Elapsed time: 23 msecs"

(time
  (doseq [i (range 40000)]))
;  "Elapsed time: 32 msecs"

(go (time
  (doseq [i (range 40000)])))
;  "Elapsed time: 94 msecs"




Clojure:
(time
  (loop [i 0]
  (when (< i 40000)
  (recur (inc i)))))
; "Elapsed time: 1.954785 msecs"

(go
  (time
    (loop [i 0]
  (when (< i 40000)
  (recur (inc i))))))
; "Elapsed time: 1.826223 msecs"

(time
  (doseq [i (range 40000)]))
; "Elapsed time: 11.048818 msecs"

(go
  (time
  (doseq [i (range 40000)])))
; "Elapsed time: 11.668555 msecs"

Robin Heggelund Hansen

unread,
Jan 10, 2015, 7:31:41 AM1/10/15
to clojur...@googlegroups.com
Interesting. What happens when you use go-loop?

Christian Weilbach

unread,
Jan 10, 2015, 8:55:52 AM1/10/15
to clojur...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 10.01.2015 13:31, Robin Heggelund Hansen wrote:
> Interesting. What happens when you use go-loop?
>
go-loop is just a macro alias for (go (loop ..)):
https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async/macros.clj#L95

Christian
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJUsS9lAAoJEKel+aujRZMkp5YH/j1XHzI45Fc7vFn3gYQNw8kL
I2gY6jM5+3m1LL2mzHG97+izJGHfzXKwZ+/I/jZBzR/4qkngU2+rQd9N5ONsv13O
kFfan1EtXbrr7Ybdi0YXBTHB8xgJOAIeal/bqJA9pX8brqG1e2Kx9oflitwOWne8
+T9RleHWn5wKilmpopKAxycpZbeEWwXWyZ+zBB7PoOcrqUTNeBqu9grT8QBsQTL/
IyKWMU6TlYoTItm8d/+USGITh7t3xjoQNvN94RmscNUInO64cDnDQ9ZD7w9NTfQv
hhFNNnDrc8SqK7RKB8vFCrvecIgus+IhBYHBNRvSw2ua4gQVC0wvcoG9kd+ZErk=
=UYCk
-----END PGP SIGNATURE-----

David Nolen

unread,
Jan 10, 2015, 10:03:50 AM1/10/15
to clojur...@googlegroups.com
This is interesting but a very low priorty problem. Putting inner loop computational work directly into go blocks like that really doesn't make much sense due to the transformations. Things might get better when ClojureScript gets the same code transformer as Clojure - they have diverged for some time.
--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
To post to this group, send email to clojur...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojurescript.

Marcus Lewis

unread,
Jan 10, 2015, 1:41:14 PM1/10/15
to clojur...@googlegroups.com
My paint function is basically

(defn paint [ctx requests]
(let [response (chan)]
(put! requests [:a :custom :request response])
(go
(let [r (<! response)]
:now-paint-the-pixels))))

My inner loop is inside a go block simply because that's where the code landed. It wasn't essential that it be inside a go block. But it wasn't wrong, either. It's just where the code made sense, because my paint function happens to consume a channel.

In case you're wondering, this canvas is a pixel-analyzing component. It's layered in front of another canvas and examines its pixels on mousemove. That's why my paint function consumes a channel -- it requests ImageData from the other component via a channel-based API.

David Nolen

unread,
Jan 10, 2015, 1:53:07 PM1/10/15
to clojur...@googlegroups.com
Wasn't saying it was wrong per se. Just code in go blocks incur overheads unacceptable for inner loops. Unlikely to change anytime soon.


On Saturday, January 10, 2015, Marcus Lewis <mrc...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages