Skip to content

Instantly share code, notes, and snippets.

@mikera
Last active September 9, 2015 08:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikera/2a3ed082b07a4f65cbcc to your computer and use it in GitHub Desktop.
Save mikera/2a3ed082b07a4f65cbcc to your computer and use it in GitHub Desktop.
Exploring overhead of var indirection in Clojure
;; =======================================================================
;; Motivation: we are interested in the performance of tiny
;; wrapper functions which look like:
;; (fn [_ x y] (g x y))
;;
;; Question: are they getting the full performance benefits of inlining?
(ns test
(:require [criterium.core :as c]))
*clojure-version*
; => {:major 1, :minor 6, :incremental 0, :qualifier nil}
;; A function defined in a var
(def g (fn [x y] (or x y))
; => #'test/g
;; Note: It's a non-dynamic var
(binding [g -] (g 1 2))
; => IllegalStateException Can't dynamically bind non-dynamic var: test/g clojure.lang.Var.pushThreadBindings (Var.java:320)
;; A small function wrapping g
(def f (fn [_ x y] (g x y)))
; => #'test/f
;; An equivalent small function not using a var
(def f2 (fn [_ x y] (or x y)))
;; ====================================================
;; Some Quick Benchmarks using Criterium
;; The cost of a fully inlined operation is low, around 1.4ns
(c/quick-bench (dotimes [i 1000] (or 1 2)))
; => Execution time mean : 1.357903 µs
;; The function going via indirect var g is much slower (around 10x slower)
(c/quick-bench (dotimes [i 1000] (f "Foo" 1 2)))
; => Execution time mean : 13.413987 µs
;; f2 version is much faster - close to fully inlined speed
(c/quick-bench (dotimes [i 1000] (f2 "Foo" 1 2)))
; => Execution time mean : 2.252220 µs
;; =====================================================
;; EXTRA NOTE:
;; We observe that performance is also much better if the
;; wrapped function is declared in lexical scope (with let)
;; rather than with var scope in the environment
(def f3
(let [h (fn [x y] (or x y))]
(fn [_ x y] (h x y))))
(c/quick-bench (dotimes [i 1000] (f3 "Foo" 1 2)))
; => Execution time mean : 3.023813 µs
@fromheten
Copy link

Interesting. Thank you for publishing this!

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