Skip to content

Instantly share code, notes, and snippets.

@lynaghk
Created February 5, 2013 01:07
Show Gist options
  • Save lynaghk/4711249 to your computer and use it in GitHub Desktop.
Save lynaghk/4711249 to your computer and use it in GitHub Desktop.
EDN v. JSON deserialization speed (including JSON 'tagged literals')
(ns scratch.main
(:use-macros [c2.util :only [p pp]]
[serialization :only [test-data]])
(:require [cljs.reader :refer [read-string]]
[goog.date.DateTime :as DateTime]
[clojure.string :as str]))
(defn parse-tagged-string
"Parse a tagged string within JSON.
Assumes string begins with a throwaway first character and there is a single space between the tag and its data."
[tagged-string]
(let [split-idx (.indexOf tagged-string " ")
tag (.substring tagged-string 1 split-idx)
d (.substring tagged-string (inc split-idx))
handler (case tag
"inst" DateTime/fromRfc822String)]
;;(pp [tag d])
(handler d)))
(defn parse-json
"Parse JSON string using native handler with 'reviver' function that hooks into parse-tagged-string.
See: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse"
[json-str]
(.parse js/JSON json-str
(fn [k v]
(if (and (string? v) (= "#" (aget v 0)))
(parse-tagged-string v)
v))))
;;;;;;;;;;;;;;;;;;;;;;;;
;;Baller Benchmarking...
(defn elapsed-millis [key f]
(let [start (js/Date.)]
(doall (f))
{key (- (js/Date.) start)}))
;;test-data is a macro that just spits out a map containing different serialized strings.
(def data (test-data 2000))
(def num-repeats 10)
(->> [(fn [] (elapsed-millis "edn vector" #(read-string (->> data :edn :vector))))
(fn [] (elapsed-millis "edn list" #(read-string (->> data :edn :list))))
(fn [] (elapsed-millis "json (no date parsing)" #(.parse js/JSON (->> data :json))))
(fn [] (elapsed-millis "json (with date parsing)" #(parse-json (->> data :json))))]
(mapcat (partial repeatedly num-repeats))
shuffle
(apply (partial merge-with +))
(map (fn [[k v]]
(str k ": " (/ v num-repeats) " ms (mean)")))
sort
(str/join "\n")
p)
;; Using adv. compilation with cljsbuild 0.3.0
;;
;; edn list: 298.5 ms (mean)
;; edn vector: 318.5 ms (mean)
;; json (no date parsing): 3.8 ms (mean)
;; json (with date parsing): 7.7 ms (mean)
@lynaghk
Copy link
Author

lynaghk commented Feb 5, 2013

@dribnet, not seeing the clojurescript reader speed difference between vector and list deserialization you mentioned. Maybe it's because https://github.com/dribnet/packer isn't using closure optimizations?

@dribnet
Copy link

dribnet commented Feb 5, 2013

That vector/list benchmark is in my strokes repo - which is currently using :optimizations :simple.

But you are right - I should have definitely made an optimized version of that test as well as the packer benchmark. Packer is currently set to whitespace/pretty-print, but the EDN in that test doesn't have any lists, just vectors - or more precisely, one long vector of maps. You can actually click on the "One Year" headings on the test page and see the raw json/edn - the links aren't obvious in my shoddy styling.

Have you seen the domina test suite settings? Luke actually builds 4 different versions of his test suite to test all 4 optimization flags. Maybe I'll try the same thing for my tests later tonight...

@dribnet
Copy link

dribnet commented Feb 5, 2013

OK, I didn't have time to look at this last night, but I'm pretty sure I know what this is. My test was on object creation, not deserialization. So for reading EDN lists vs vectors, it probably doesn't matter since parsing will take an order or magnitude or two longer than object creation. But I'm willing to bet a pint of portland's finest stout that if you re-run your tests looking list vs. vector creation only, you'll see a 100 to 1 difference on smallish sequences.

Incidentally, I was using benchmark.js in my tests and it worked well. It seems very well designed and purportedly avoids most potential pitfalls of benchmarking patterns with crazy function body extraction. I know @ztellman ❤️ ❤️ ❤️ criterium, and benchmark.js seems like a close analogue in the js space.

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