-
-
Save lynaghk/3335154 to your computer and use it in GitHub Desktop.
(ns crossfilter.busses | |
(:use-macros [c2.util :only [p pp]] | |
[clojure.core.match.js :only [match]] | |
[crossfilter.macros :only [subscribe!]]) | |
(:require [clojure.string :as str] | |
[goog.pubsub.PubSub :as goog.pubsub.PubSub] | |
[goog.object :as gobj])) | |
(set! *print-fn* #(.log js/console %)) | |
(def n "Number of messages" 100000) | |
(def m "Number of subscribers" 20) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;Hand-rolled regex-based pubsub | |
(let [!subscribers (js-obj) | |
publish! (fn [topic & args] | |
(gobj/forEach !subscribers | |
(fn [re fs] | |
(when (.match topic re) | |
(.forEach fs #(apply % args)))))) | |
subscribe! (fn [topic f] | |
(let [topic-re (re-pattern (str "^" (-> topic | |
(str/replace "*" "[^/]*")) | |
"$"))] | |
(aset !subscribers | |
topic-re | |
(let [a (or (aget !subscribers topic-re) | |
(array))] | |
(.push a f) | |
a))))] | |
(dotimes [_ m] | |
(subscribe! "my-topic" (fn []))) | |
(print "approx pubsub") | |
(time | |
(dotimes [i n] | |
(publish! "my-topic")))) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;Google Closure's exact string pubsub | |
(let [chan (goog.pubsub.PubSub.)] | |
(dotimes [_ m] | |
(.subscribe chan "my-topic" (fn []))) | |
(print "goog.pubsub") | |
(time | |
(dotimes [i n] | |
(.publish chan "my-topic")))) | |
;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;Core.match-based pubsub | |
(def *!subscribers* (atom [])) | |
(defn publish! [m] | |
(doseq [s @*!subscribers*] | |
(s m))) | |
;; Tested in CLJS; this macro defined in a CLJ file. | |
;; | |
;; (defmacro subscribe! [pattern & body] | |
;; `(swap! ~'*!subscribers* conj | |
;; (fn [m#] | |
;; (match [m#] | |
;; [~pattern] (do ~@body))))) | |
(do (print "match pubsub with string matching") | |
(dotimes [_ m] | |
(subscribe! "my-topic" | |
"no-op")) | |
(time | |
(dotimes [i n] | |
(publish! "my-topic")))) | |
(reset! *!subscribers* []) | |
(do (print "match pubsub with map destructuring") | |
(dotimes [_ m] | |
(subscribe! {:with x} | |
"no-op")) | |
(time | |
(dotimes [i n] | |
(publish! {:with "destructuring"})))) | |
;;Results from a MacBook Air, Closure advanced mode compilations. | |
;; approx pubsub | |
;; "Elapsed time: 16686 msecs" | |
;; goog.pubsub | |
;; "Elapsed time: 1417 msecs" | |
;; match pubsub with string matching | |
;; "Elapsed time: 583 msecs" | |
;; match pubsub with map destructuring | |
;; "Elapsed time: 787 msecs" |
How is that even possible?!
I should totally try this on Shoreleave's base pubsub protocol implementations.
Yeah, right? Maybe there's some laziness tricks going on, but I've run this using counters inside of the subscribing functions to make sure they're getting called, and it appears to be working.
Maybe things will get hella slower with more complicated pattern matching?
@ohpauleez Also, WTF is up with the terrible regex performance? I moved everything to use JS data structures over ClojureScript's seqs, but it's still hella slow.
Uh oh. Turns out core.match will match variables in a map to nil.
That is, (subscribe! {:with x} ...))
will get called if any map is published, binding x
to nil.
Patched; awaiting merge into master: https://github.com/lynaghk/core.match/tree/issue-52
@dnolen Content-based map-destructuring pubsub via core.match in CLJS is faster than Google's exact string matching pubsub. Wait, what? Awesome.