-
-
Save cgrand/126676 to your computer and use it in GitHub Desktop.
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
(set! *warn-on-reflection* true) | |
(ns slow | |
(:import [java.nio ByteBuffer Buffer])) | |
(defn #^ints bytes-to-ints [#^bytes bs] | |
(let [#^ints is (make-array Integer/TYPE (alength bs))] | |
(loop [i (int 0)] | |
(if (< i (alength bs)) | |
(do | |
(aset is i (bit-and 0xFF (int (aget bs i)))) | |
(recur (inc i))) | |
is)))) | |
(defmacro let-> [obj bindings & body] | |
(let [obj-sym (gensym "obj")] | |
`(let [~obj-sym ~obj | |
~@(mapcat (fn [[k v]] [k (list `-> obj-sym v)]) | |
(partition 2 bindings))] | |
~@body))) | |
(defn skip [#^Buffer buf n] | |
(.position buf (+ (.position buf) n))) | |
(defn #^"[S" get-shorts [#^ByteBuffer buf n] | |
(let [#^shorts ss (make-array Short/TYPE n)] | |
(-> buf .asShortBuffer (.get ss)) | |
(skip buf (* 2 n)) | |
ss)) | |
(defn #^"[B" get-bytes [#^ByteBuffer buf n] | |
(let [#^bytes bs (make-array Byte/TYPE n)] | |
(.get buf bs) | |
bs)) | |
(defn #^ints get-unsigned-bytes [#^ByteBuffer buf n] | |
(bytes-to-ints (get-bytes buf n))) | |
(defn get-unsigned-byte [#^ByteBuffer buf] | |
(bit-and 0xFF (int (.get buf)))) | |
(defn #^String get-string | |
([buf bytes-count] | |
(let [s (-> buf (get-bytes bytes-count) (String. "US-ASCII")) | |
i (.indexOf s 0)] | |
(if (neg? i) s (.substring s 0 i)))) | |
([buf] (get-string buf 1))) | |
(def *readers* {'Byte `get-bytes | |
:unsigned-byte `get-unsigned-bytes | |
'String `get-string | |
'Short `get-shorts}) | |
(def *read1* {'Byte `.get | |
:unsigned-byte `get-unsigned-byte | |
'String `get-string | |
'Short `.getShort | |
'Integer `.getInt}) | |
(defn compile-spec [[len type f]] | |
(cond | |
f `(let-> [data# ~(compile-spec [len type])] | |
(~f data#)) | |
(vector? len) | |
`(let-> [n# ~(compile-spec len) | |
data# (~(*readers* type) n#)] | |
data#) | |
(= 1 len) | |
(*read1* type) | |
:else | |
`(~(*readers* type) ~len))) | |
(defmacro parse-buffer [buf & field-specs] | |
(let [syms+field-specs (for [f field-specs] [(gensym) f]) | |
result-map (into {} | |
(for [[sym [key]] syms+field-specs :when key] [key sym]))] | |
`(let-> ~buf [~@(mapcat (fn [[sym [field & spec]]] [sym (compile-spec spec)]) | |
syms+field-specs)] | |
~result-map))) | |
(defn run | |
[#^ByteBuffer buf] | |
(parse-buffer buf | |
[:byte 1 :unsigned-byte] | |
[:short [1 :unsigned-byte] Short #(reduce + %)] | |
[:integer 1 Integer] | |
[:string 10 String] | |
[nil 10 Byte])) | |
(defn bench [] | |
(let [arr (into-array Byte/TYPE (map byte [7 ; 1 Byte | |
3 0 97 0 98 0 99 ; 3 Shorts | |
0 0 100 100 ; 1 Integer | |
65 66 67 68 69 70 71 72 73 74 ; 10 String | |
0 0 0 0 0 0 0 0 0 0 ; 10 ignored | |
]))] | |
(time | |
(dotimes [_ 10000] | |
(let [buf (ByteBuffer/wrap arr)] | |
(run buf)))))) | |
(comment | |
; this code | |
slow=> (dotimes [_ 10] (bench)) | |
"Elapsed time: 53.092781 msecs" | |
"Elapsed time: 42.550939 msecs" | |
"Elapsed time: 53.071829 msecs" | |
"Elapsed time: 20.975012 msecs" | |
"Elapsed time: 20.679165 msecs" | |
"Elapsed time: 22.510123 msecs" | |
"Elapsed time: 18.797361 msecs" | |
"Elapsed time: 22.059787 msecs" | |
"Elapsed time: 44.361783 msecs" | |
"Elapsed time: 19.971812 msecs" | |
; code at http://gist.github.com/126661 | |
slow=> (dotimes [_ 10] (bench)) | |
"Elapsed time: 449.096743 msecs" | |
"Elapsed time: 451.16153 msecs" | |
"Elapsed time: 269.256009 msecs" | |
"Elapsed time: 302.183099 msecs" | |
"Elapsed time: 275.283032 msecs" | |
"Elapsed time: 289.821472 msecs" | |
"Elapsed time: 302.892127 msecs" | |
"Elapsed time: 271.186981 msecs" | |
"Elapsed time: 289.20184 msecs" | |
"Elapsed time: 272.354447 msecs" | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment