speculative

https://github.com/borkdude/speculative
slipset 2018-11-05T06:51:07.000600Z

I would imagine that if @mfikes lookted at this for about five minutes, a lot of these numbers could be halved.

borkdude 2018-11-05T07:26:02.004200Z

We already looked at this for CLJS-2956, where = caused maximum callstack exceeded. The question is if you want to optimize spec for self spec’ing: you basically have to write entire spec in a different language if you want to solve this problem once and for all (JavaScript).

borkdude 2018-11-05T07:30:09.005600Z

I tried this:

apply' (fn [f args]
                 (if (and (nil? args)
                          pure-variadic?)
                   (.cljs$core$IFn$_invoke$arity$variadic f)
                   (do (m/unstrument `apply)
                       (apply f args)
                       #_(m/instrument `apply))))
in spec-checking-fn, but that didn’t make
(stest/instrument `apply)
(apply + 1 2 [3])
work in user code 🙂

borkdude 2018-11-05T09:17:34.006800Z

Disabled apply instrumentation in test on cljs. Pushed to cljs-1.10.439 branch.

borkdude 2018-11-05T09:18:21.007100Z

Tests pass on clj and planck, but not on cljs. Still seeing CLJS-2955 despite @mfikes’s patches applied.

borkdude 2018-11-05T09:55:07.000400Z

it appears to be an issue with the cljs-test-runner. When I run the tests using our own script, they are fine

borkdude 2018-11-05T10:17:34.001Z

Merged the cljs-1.10.439 branch and reverted to nashorn instead of the test-runner.

borkdude 2018-11-05T11:48:28.000200Z

back to node now. it’s so much faster

borkdude 2018-11-05T11:49:04.000500Z

build / test time goes from 2:28 to 0:52 again

borkdude 2018-11-05T13:26:00.001700Z

@slipset one note, now that we’re depending on a git dep (temporary patches for CLJS), the lein setup is broken now (especially lein deploy). I saw that you made a thing for clj to deploy to clojars. Maybe we could use that?

slipset 2018-11-05T13:26:29.002100Z

Yup, haven’t gotten around to that yet 🙂

borkdude 2018-11-05T13:26:36.002300Z

🙂

mfikes 2018-11-05T13:49:44.004400Z

If there are specs that require non-released ClojureScript versions in order to work, perhaps they could be conditionally enabled, either using :closure-defines or goog.string/compareVersions comparing *clojurescript-version*. That way, Speculative can be more readily used in downstream projects.

borkdude 2018-11-05T14:47:29.007Z

sounds like an idea. but specs only become errors in certain situations: e.g. when you instrument them, or generatively test them.

borkdude 2018-11-05T14:47:38.007200Z

so it depends?

borkdude 2018-11-05T14:52:22.008Z

the overhead of internal spec calls using the spec-checking-fn may be OK. I discovered that conforming only happens the amount of times you call the function yourself when instrumenting.

(def counter (atom 0))

(defn fixture [f]
  (reset! counter 0)
  (f))

(t/use-fixtures :each fixture)

(deftest =-cost-test
  (s/fdef clojure.core/=
    :args (fn [_#]
            (println _#)
            (swap! counter inc)
            _#))
  (with-instrumentation `=
    (println "START")
    (apply = 1 [1])
    (apply = 2 [1])
    (println "COST:" @counter))
  (println "AFTER INSTRUMENT")
  (println @counter))

borkdude 2018-11-05T14:53:01.008800Z

The cost for = here was 2, but the wrapped version of = got called 73 times. So there isn’t the overhead of conforming each internal call of =, that’s good. But there is still some small overhead which may be OK.

borkdude 2018-11-05T15:08:46.009400Z

The only way to know is to do a perf test with and without. 🙂 http://blog.fikesfarm.com/posts/2017-11-18-clojurescript-performance-measurement.html

borkdude 2018-11-05T15:14:45.009800Z

cljs.user=> (simple-benchmark [] (= 1 2 3 4 5 6) 100000)
[], (= 1 2 3 4 5 6), 100000 runs, 28 msecs
nil
cljs.user=> (require '[speculative.core])
nil
cljs.user=> (stest/instrument `=)
[cljs.core/=]
cljs.user=> (simple-benchmark [] (= 1 2 3 4 5 6) 100000)
[], (= 1 2 3 4 5 6), 100000 runs, 31902 msecs
nil

borkdude 2018-11-05T15:15:59.010400Z

using this method, we could score the specs (31902/28 = 1139) and give it a performance cost.

borkdude 2018-11-05T15:18:01.011200Z

so we could give users a choice: instrumenting =, go ahead (and given it’s accepting nature there’s almost no way to make it crash so not very useful), but here’s the cost.

borkdude 2018-11-05T15:20:44.011500Z

hmm:

$ plk -A:test:repl
cljs.user=> (require '[speculative.core])
nil
cljs.user=> (stest/instrument `str)
Maximum call stack size exceeded.

borkdude 2018-11-05T16:00:20.012400Z

merge ‘only’ has a cost factor of 82 and it’s also a pretty useful spec. so a typical dev profile would select that one