(add-classpath "file:///Users/ragge/Projects/clojure/redis-clojure/redis-clojure.jar") (ns benchmarks.clojure (:use clojure.contrib.pprint) (:require redis)) (defstruct benchmark-options :host :port :db :clients :requests :key-size :keyspace-size :data-size) (defstruct client :id :request-times :requests-performed :requests-per-second) (defstruct result :options :clients :total-time :requests) (defmacro defbenchmark [name & body] (let [benchmark-name (symbol (str name "-benchmark"))] `(def ~(with-meta benchmark-name {:benchmark true}) (fn ~benchmark-name [client# options# result#] (redis/with-server {:host (options# :host) :port (options# :port) :db (options# :db)} (let [requests# (:requests options#) requests-done# (:requests result#)] (loop [requests-performed# 0 request-times# []] (if (>= @requests-done# requests#) (assoc client# :request-times request-times# :requests-performed requests-performed#) (do (let [start# (System/nanoTime)] ~@body (let [end# (System/nanoTime) elapsed# (/ (float (- end# start#)) 1000000.0)] (dosync (commute requests-done# inc)) (recur (inc requests-performed#) (conj request-times# elapsed#))))))))))))) (defbenchmark ping (redis/ping)) (defbenchmark get (redis/get (str "key-" (rand-int 1000)))) (defbenchmark set (redis/set (str "key-" (rand-int 1000)) "blahojga!")) (defbenchmark exists-set-and-get (let [key (str "key-" (rand-int 100))] (redis/exists key) (redis/set key "blahongaa!") (redis/get key))) (def *default-options* (struct-map benchmark-options :host "127.0.0.1" :port 6379 :db 15 :clients 4 :requests 10000)) (defn create-clients [options] (for [id (range (:clients options))] (agent (struct client id)))) (defn create-result [options clients] (let [result (struct result options clients 0 (ref 0))] result)) (defn requests-by-ms [clients] (let [all-times (apply concat (map #(:request-times (deref %)) clients)) all-times-in-ms (map #(int (/ % 1)) all-times)] (sort (reduce (fn [m time] (if (m time) (assoc m time (inc (m time))) (assoc m time 1))) {} all-times-in-ms)))) (defn report-request-times [clients requests] (let [requests-dist (map #(let [perc (* 100 (/ (last %) requests))] (conj % perc)) (requests-by-ms clients))] (dorun (map #(println (format "%.2f%% < %d ms" (float (last %)) (inc (first %)))) requests-dist)))) (defn report-client-rps [client] (let [{:keys [id requests-performed request-times]} @client] (when (< 0 requests-performed) (let [total-time (apply + request-times) requests-per-second (/ (float requests-performed) total-time)] (println total-time) (println (format "Client %d: %f rps" id (float requests-per-second))))))) (defn report-result [result] (let [{:keys [clients options]} result name (:name result) time (:total-time result) time-in-seconds (/ time 1000) requests (deref (:requests result)) requests-per-second (/ requests time-in-seconds) ] (do (println (format "====== %s =====\n" name)) (println (format " %d requests completed in %f seconds\n" requests time-in-seconds)) (println (format " %d parallel clients\n" (:clients options))) ;(report-request-times clients requests) ;(dorun (map report-client-rps clients)) (println (format "%f requests per second\n\n" requests-per-second)) ) ) ) (defn run-benchmark [fn options] (let [clients (create-clients options) result (create-result options clients) start (System/nanoTime)] (dorun (map #(send-off % fn options result) clients)) (apply await clients) (let [elapsed (/ (double (- (System/nanoTime) start)) 1000000.0)] (dorun (map #(when (agent-errors %) (pprint (agent-errors %))) clients)) (assoc result :name (str fn) :options options :clients clients :total-time elapsed)))) (defn find-all-benchmarks [ns] (filter #(:benchmark (meta %)) (vals (ns-map ns)))) (defn run-and-report [fn options] (let [result (run-benchmark fn options)] (report-result result))) (defn run-all-benchmarks [ns] (let [benchmarks (find-all-benchmarks ns)] (dorun (map #(run-and-report % *default-options*) benchmarks)))) ;(run-all-benchmarks) ;(report-result (run-benchmark ping-benchmark *default-options*)) ;(run-benchmark get-benchmark *default-options*)