Git Product home page Git Product logo

jmh-clojure's Introduction

Clojars Project

Dependency and version information

Click to show

Leiningen

[jmh-clojure "0.4.1"]

tools.deps

{jmh-clojure/jmh-clojure {:mvn/version "0.4.1"}}

Maven

<dependency>
  <groupId>jmh-clojure</groupId>
  <artifactId>jmh-clojure</artifactId>
  <version>0.4.1</version>
</dependency>

JDK versions 8 to 18 and Clojure versions 1.7 to 1.11 are currently tested against.

What is it?

This library provides a data-oriented API to JMH, the Java Microbenchmark Harness.

JMH is developed by OpenJDK JVM experts and goes to great lengths to ensure accurate benchmarks. Benchmarking on the JVM is a complex beast and, by extension, JMH takes a bit of effort to learn and use properly. That being said, JMH is very robust and configurable. If you are new to JMH, I would recommend browsing the sample code and javadocs before using this library.

If you a need simpler, less strenuous tool, I would suggest looking at the popular criterium library.

Quick start

As a simple example, let's say we want to benchmark our fn that gets the value at an arbitrary indexed type. Of course, the built in nth already does this, but we can't extend nth to existing types like java.nio.ByteBuffer, etc.

(ns demo.core)

(defprotocol ValueAt
  (value-at [x idx]))

(extend-protocol ValueAt
  clojure.lang.Indexed
  (value-at [i idx]
    (.nth i idx))
  CharSequence
  (value-at [s idx]
    (.charAt s idx))
  #_...)

Benchmarks are usually described in data and are fully separated from definitions. The reason for this is twofold. First, decoupling is generally good design practice. And second, it allows us to easily take advantage of JMH process isolation (forking) for reliability and accurracy. More on this later.

For repeatability, we'll place the following data in a benchmarks.edn resource file in our project. (Note that using a file is not a requirement, we could also specify the same data in Clojure. The :fn key values would need to be quoted in that case, however.)

{:benchmarks
 [{:name :str, :fn demo.core/value-at, :args [:state/string, :state/index]}
  {:name :vec, :fn demo.core/value-at, :args [:state/vector, :state/index]}]

 :states
 {:index {:fn (partial * 0.5), :args [:param/count]} ;; mid-point
  :string {:fn demo.utils/make-str, :args [:param/count]}
  :vector {:fn demo.utils/make-vec, :args [:param/count]}}

 :params {:count 10}}

I have omitted showing the demo.utils namespace for brevity, it is defined here if interested.

The above data should be fairly easy to understand. It is also a limited view of what can be specified. The sample file provides a complete reference and explanation.

Now to run the benchmarks. We'll start a REPL in our project and evaluate the following. Note that we could instead use lein-jmh or one of the other supported tools to automate this entire process.

(require '[jmh.core :as jmh]
         '[clojure.java.io :as io]
         '[clojure.edn :as edn])

(def bench-env
  (-> "benchmarks.edn" io/resource slurp edn/read-string))

(def bench-opts
  {:type :quick
   :params {:count [31 100000]}
   :profilers ["gc"]})

(jmh/run bench-env bench-opts)
;; => ({:name :str, :params {:count 31},     :score [1.44959801438209E8 "ops/s"], #_...}
;;     {:name :str, :params {:count 100000}, :score [1.45485370497829E8 "ops/s"]}
;;     {:name :vec, :params {:count 31},     :score [1.45550038851249E8 "ops/s"]}
;;     {:name :vec, :params {:count 100000}, :score [8.5783753539823E7 "ops/s"]})

Note: due to the way jmh-clojure works, the *compile-path* directory should exist and be on your classpath before benchmarking. This is automated by tools like Leiningen. For tools.deps, see here.

The run fn takes a benchmark environment and an optional map. We select the :quick type: an alias for some common options. We override our default :count parameter sequence to measure our fn against small and large inputs. We also enable the gc profiler.

Notice how we have four results: one for each combination of parameter and benchmark fn. For this example, we have omitted lots of additional result map data, including the profiler information.

Note that the above results were taken from multiple runs, which is always a good practice when benchmarking.

Alternate ways to run

Benchmarking expressions or fns manually without the data specification is also supported. For example, the run-expr macro provides an interface similar to criterium, and allows benchmarking of code that only resides in memory (that you are updating in a REPL, for example), rather than on disk (loadable via require). However, this forgoes JMH process isolation. For more on why benchmarking this way on the JVM can be sub-optimal, see here.

Tooling support

This library can be used directly in a bare REPL, as shown above, or standalone via tools.deps. For a more robust experience, see the jmh-clojure-task project. This companion library provides some additional convenience features like sorting, table output, easy uberjar creation, and more. It can be easily integrated with tools like Leiningen.

More information

As previously mentioned, please see the sample file for the complete benchmark environment reference. For run options, see the docs. Also, see the wiki for additional examples and topics.

The materials for a talk I gave at a London Clojurians online meetup are also available here. A video capture of the event can also be viewed on YouTube.

Running the tests

lein test

Or, lein test-all for all supported Clojure versions.

License

Copyright © 2017-2024 Justin Conklin

Distributed under the Eclipse Public License, the same as Clojure.

jmh-clojure's People

Contributors

jgpc42 avatar puredanger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jmh-clojure's Issues

Valid clj keyword benchmark names are munged to invalid Java method names

Dots are allowed in clj keywords, but if I name a benchmark something like :manifold.speederino.bus-test, jmh-clojure generates a .class with a method name like _000_manifold.speederino.bus_test, which is invalid in Java. I haven't checked, but I'll bet other valid keyword chars are munged incorrectly, too.

"package org.openjdk.jmh.annotations does not exist" when using boot

Hi,
I’m trying to get started with this library with boot. I’m following the steps in your quick start section, but I’m getting this error when I try to invoke (jmh/run bench-env bench-opts. Complete stack trace follows.


  Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (13 frames hidden)

1. Unhandled clojure.lang.ExceptionInfo
   java compilation error:
   /jmh1533485451967/generated/bench_0_hello_run_jmhTest.java: line 8:
   package org.openjdk.jmh.annotations does not exist ... and 99 more
   {:errors
    ({:line 8,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 9,
      :message "package org.openjdk.jmh.runner does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 10,
      :message "package org.openjdk.jmh.infra does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 11,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 12,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 13,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 14,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 15,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 16,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 17,
      :message "package org.openjdk.jmh.util does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 18,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 19,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 20,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 21,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 22,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 23,
      :message "package org.openjdk.jmh.annotations does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 24,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 25,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 27,
      :message "package org.openjdk.jmh.infra does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 28,
      :message "package org.openjdk.jmh.infra does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 29,
      :message "package org.openjdk.jmh.infra does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 30,
      :message "package org.openjdk.jmh.infra does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 31,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 32,
      :message "package org.openjdk.jmh.results does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 33,
      :message "package org.openjdk.jmh.runner does not exist",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 56,
      :message
      "cannot find symbol\n  symbol:   class BenchmarkParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 57,
      :message
      "cannot find symbol\n  symbol:   class IterationParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 58,
      :message
      "cannot find symbol\n  symbol:   class ThreadParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 59,
      :message
      "cannot find symbol\n  symbol:   class Blackhole\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 60,
      :message
      "cannot find symbol\n  symbol:   class Control\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 62,
      :message
      "cannot find symbol\n  symbol:   class InfraControl\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 62,
      :message
      "cannot find symbol\n  symbol:   class ThreadParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 62,
      :message
      "cannot find symbol\n  symbol:   class BenchmarkTaskResult\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class InfraControl\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class RawResults\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class BenchmarkParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class IterationParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class ThreadParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class Blackhole\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 140,
      :message
      "cannot find symbol\n  symbol:   class Control\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 154,
      :message
      "cannot find symbol\n  symbol:   class InfraControl\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 154,
      :message
      "cannot find symbol\n  symbol:   class ThreadParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 154,
      :message
      "cannot find symbol\n  symbol:   class BenchmarkTaskResult\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class InfraControl\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class RawResults\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class BenchmarkParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class IterationParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class ThreadParams\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class Blackhole\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     {:line 232,
      :message
      "cannot find symbol\n  symbol:   class Control\n  location: class jmh1533485451967.generated.bench_0_hello_run_jmhTest",
      :source
      "/jmh1533485451967/generated/bench_0_hello_run_jmhTest.java"}
     ...)}

                  core.clj: 4739  clojure.core/ex-info
                  core.clj: 4739  clojure.core/ex-info
                  java.clj:   28  jmh.java/compile-error
                  java.clj:   14  jmh.java/compile-error
                  java.clj:   70  jmh.java/compile
                  java.clj:   39  jmh.java/compile
              generate.clj:   76  jmh.generate/process
              generate.clj:   45  jmh.generate/process
              generate.clj:   92  jmh.generate/write/fn
              generate.clj:   90  jmh.generate/write
              generate.clj:   78  jmh.generate/write
                  core.clj: 2561  clojure.core/comp/fn
                  core.clj: 2562  clojure.core/comp/fn
                  core.clj:   93  jmh.core/run
                  core.clj:   14  jmh.core/run
                      REPL:   64  boot.user/eval10170
                      REPL:   64  boot.user/eval10170
             Compiler.java: 7062  clojure.lang.Compiler/eval
             Compiler.java: 7025  clojure.lang.Compiler/eval
                  core.clj: 3206  clojure.core/eval
                  core.clj: 3202  clojure.core/eval
                  main.clj:  243  clojure.main/repl/read-eval-print/fn
                  main.clj:  243  clojure.main/repl/read-eval-print
                  main.clj:  261  clojure.main/repl/fn
                  main.clj:  261  clojure.main/repl
                  main.clj:  177  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  clojure.tools.nrepl.middleware.interruptible-eval/evaluate/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  657  clojure.core/apply
                  core.clj: 1965  clojure.core/with-bindings*
                  core.clj: 1965  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   85  clojure.tools.nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   55  clojure.tools.nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  222  clojure.tools.nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
    interruptible_eval.clj:  190  clojure.tools.nrepl.middleware.interruptible-eval/run-next/fn
                  AFn.java:   22  clojure.lang.AFn/run
   ThreadPoolExecutor.java: 1135  java.util.concurrent.ThreadPoolExecutor/runWorker
   ThreadPoolExecutor.java:  635  java.util.concurrent.ThreadPoolExecutor$Worker/run
               Thread.java:  844  java.lang.Thread/run

byte-code level incompatibility with Java <= 13

Hi,

The recent update of jmh-clojure specifically version 0.3.0 contains pre-compiled classes in the jmh-clojure-0.3.0.jar jar file which are compiled with Java 14.
This causes an incompatibility with anything that is running with Java 13 or earlier.

This is the error i get:

Syntax error (UnsupportedClassVersionError) compiling at (jmh/util.clj:1:1).
io/github/jgpc42/jmh/DelegateClassLoader has been compiled by a more recent version of the Java Runtime (class file version 58.0), this version of the Java Runtime only recognizes class file versions up to 57.0

the jmh-clojure jar contains a few precompiled classes

jar -tvf ~/.m2/repository/jmh-clojure/jmh-clojure/0.3.0/jmh-clojure-0.3.0.jar
[...]
  1405 Wed Jul 15 11:23:36 BST 2020 io/github/jgpc42/jmh/DelegateClassLoader.class
   927 Wed Jul 15 11:23:36 BST 2020 io/github/jgpc42/jmh/sample/Benchmarks.class
   629 Wed Jul 15 11:23:36 BST 2020 io/github/jgpc42/jmh/sample/Counters.class
  2125 Wed Jul 15 11:23:36 BST 2020 io/github/jgpc42/jmh/Util.class
[...]

the version of these classes is 58:

$ javap --class-path ~/.m2/repository/jmh-clojure/jmh-clojure/0.3.0/jmh-clojure-0.3.0.jar -v io.github.jgpc42.jmh.Util | head
Classfile jar:file:///Users/bruno/.m2/repository/jmh-clojure/jmh-clojure/0.3.0/jmh-clojure-0.3.0.jar!/io/github/jgpc42/jmh/Util.class
  Last modified 15 Jul 2020; size 2125 bytes
  SHA-256 checksum dc232c06a3c435626351cbcf2db6e4d8746da841463e4e9a3582a44f6c80e208
  Compiled from "Util.java"
public final class io.github.jgpc42.jmh.Util
  minor version: 0
  major version: 58

Is this accidental or you just want to drop the compatibility with older Java version?

If it is accidental I suggest adding the desired output version in the project.clj as follow:

  :javac-options ["-target" "1.8" "-source" "1.8"]

Otherwise please document the compatibility change in your CHANGELOG.md

Examples in README fail due to clojure.lang.Named cast exception

Hi,

I've been waiting for this plugin/library for a long long time!

I hit some issues running the README example, which I managed to fix by passing in symbols for the env rather than fns.

{:benchmarks
 [{:name :str, :fn demo.core/value-at, :args [:state/string, :state/index]}
  {:name :vec, :fn demo.core/value-at, :args [:state/vector, :state/index]}]

 :states
 {:index {:fn (partial * 0.5), :args [:param/count]}
  :string {:fn demo.utils/make-str, :args [:param/count]}
  :vector {:fn demo.utils/make-vec, :args [:param/count]}}

 :params {:count 10}}
Uncaught exception, not in assertion.                                 
expected: nil                                                         
  actual: java.lang.ClassCastException: clojure.core$partial$fn__5380 cannot be cast to clojure.lang.Named
 at clojure.core$namespace.invokeStatic (core.clj:1594)                                                                                     
    clojure.core$namespace.invoke (core.clj:1594)                     
    jmh.state$fn_load.invokeStatic (state.clj:147)                    
    jmh.state$fn_load.invoke (state.clj:139)                          
    clojure.core$map$fn__5406.invoke (core.clj:2733)                  
    clojure.lang.LazySeq.sval (LazySeq.java:40)                       
    clojure.lang.LazySeq.seq (LazySeq.java:49)                        
    clojure.lang.RT.seq (RT.java:525)                                                                                                       
    clojure.core$seq__4944.invokeStatic (core.clj:137)                                                                                      
    clojure.core$apply.invokeStatic (core.clj:652)                                                                                          
    clojure.core$mapcat.invokeStatic (core.clj:2763)                                                                                        
    clojure.core$mapcat.doInvoke (core.clj:2763)                      
    clojure.lang.RestFn.invoke (RestFn.java:423)                                                                                            
    jmh.state$class_type.invokeStatic (state.clj:263)                                                                                       
    jmh.state$class_type.invoke (state.clj:259)                       
    clojure.core$map$fn__5406.invoke (core.clj:2735)
    clojure.lang.LazySeq.sval (LazySeq.java:40)                       
    clojure.lang.LazySeq.seq (LazySeq.java:56)                        
    clojure.lang.ChunkedCons.chunkedNext (ChunkedCons.java:59)        
    clojure.lang.ChunkedCons.next (ChunkedCons.java:43)                                                                                     
    clojure.lang.PersistentVector.create (PersistentVector.java:73)   
    clojure.lang.LazilyPersistentVector.create (LazilyPersistentVector.java:44)

Implicit default selectors to simplify :select usage

The selector mechanism is very powerful and general. However, the most
frequent selector use in my experience, ‘run just benchmarks x and y’,
is not convenient to set up: I have to define an explicit selector
predicate for each benchmark.

:benchmarks
[{:name :foo, :fn my.app/foo}
 {:name :bar, :fn my.app/bar}
 {:name :baz, :fn my.app/baz}
 ,,,
 ]

:selectors
{:foo (comp #{:foo} :name)
 :bar (comp #{:bar} :name)
 :baz (comp #{:baz} :name)
 ,,,
 }

Unless I am overlooking something obvious each benchmark needs a
selector mirroring it. That’s boilerplate configuration.

Would it make sense to provide an implicit default selector for each
benchmark, perhaps based on :name? (Enabling :select [:foo]
directly, where :foo is the name of the benchmark.)

I haven’t thought it through, just bringing this to your attention …
last missing piece for me before I am 100% happy with jmh-clojure,
promise.

Support Clojure 1.9 clj tool

Clojure 1.9 was just released with the addition of the clj command line tool. It let's you declare dependencies in and start a repl or run a -main entrypoint (guide).

I am just getting started with lein-jmh, but am now wondering about switching away from Leiningen entirely, now that this tool has been released. Do you have any thoughts on how much work it would be to use this library with that tool?

NullPointerException when any benchmark is not matched by :selector predicate

The selector functionality activated via the :select option seems to
be broken: a NullPointerException is thrown whenever the active
selector’s predicate returns false for a benchmark.

NullPointerException
        jmh.exec/run* (exec.clj:95)
        jmh.exec/run* (exec.clj:74)
        jmh.exec/run (exec.clj:208)
        jmh.exec/run (exec.clj:181)
        clojure.core/comp/fn--4727 (core.clj:2460)
        clojure.core/comp/fn--4727 (core.clj:2460)
        clojure.core/comp/fn--4727 (core.clj:2461)
        jmh.core/run (core.clj:87)
        jmh.core/run (core.clj:14)
        jmh-example2.core/eval10215 (form-init3157860254002531894.clj:1)
        jmh-example2.core/eval10215 (form-init3157860254002531894.clj:1)
        clojure.lang.Compiler.eval (Compiler.java:6927)

To reproduce, run a benchmark with the option :select :example and the
following selector specification.

:selectors
{:example (constantly false)}

Support params in :options map

I've got a jmh.edn with quite a few benchmarks that are repeated internally (since the functions are usually used to build up lists in the 'real' code).

I've controlled the number of times these are run with a parameter, but I'd also like to be able to apply that to the :ops-per-invocation option on these tests, i.e. :options {:ops-per-invocation :param/times-to-run.

Best way to benchmark a variable number of arguments

Hello,
I'm benchmarking code I wrote which does some inlining at macro expansion time.
A simple example would be get-in
My inlining code runs if the keys can be expanded at compile time, i.e.

(get-in m ks) ;; baseline
(inline/get-in m ks) ;; will run like regular get-in
(inline/get-in m [k1 k2 k3]) ;; will inline

I want to run benchmarks which will scan over the keys depth. To bench clojure.core/get-in I just need:

{:name :core/get-in :fn clojure.core/get-in :args [:state/map :state/ks]}

However, to get the benefits of inlining, I need to build some wrapper around my code:

(defn inline-get-in
  ([m k1] (inline/get-in m [k1]))
  ([m k1 k2] (inline/get-in m [k1 k2]))
  ([m k1 k2 k3] (inline/get-in m [k1 k2 k3]))
  ([m k1 k2 k3 k4] (inline/get-in m [k1 k2 k3 k4])))

I don't want to use args like [m [k1 k2 ,,,]] as it will incur the destructuring overhead at run time.
Another option is unrolling individual keys to states, but then I will need to duplicate benchmarks.

Any tips or directions would be very welcome
Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.