Created
August 12, 2012 22:56
-
-
Save lynaghk/3335154 to your computer and use it in GitHub Desktop.
content-based pubsub via core.match. Faster than I expected!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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" |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@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.