Skip to content

Instantly share code, notes, and snippets.

@lynaghk
Created August 12, 2012 22:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lynaghk/3335154 to your computer and use it in GitHub Desktop.
Save lynaghk/3335154 to your computer and use it in GitHub Desktop.
content-based pubsub via core.match. Faster than I expected!
(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"
@lynaghk
Copy link
Author

lynaghk commented Aug 13, 2012

@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.

@lynaghk
Copy link
Author

lynaghk commented Aug 13, 2012

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.

@lynaghk
Copy link
Author

lynaghk commented Aug 14, 2012

Patched; awaiting merge into master: https://github.com/lynaghk/core.match/tree/issue-52

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment