This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-10-23
Channels
- # aws-lambda (1)
- # bangalore-clj (3)
- # beginners (80)
- # boot (8)
- # clojars (1)
- # clojure (200)
- # clojure-dev (37)
- # clojure-greece (26)
- # clojure-italy (11)
- # clojure-norway (3)
- # clojure-russia (14)
- # clojure-spec (21)
- # clojure-uk (30)
- # clojurescript (50)
- # core-logic (10)
- # core-matrix (1)
- # cursive (15)
- # data-science (21)
- # datomic (45)
- # devcards (2)
- # emacs (4)
- # fulcro (12)
- # garden (2)
- # jobs (5)
- # juxt (1)
- # lambdaisland (1)
- # leiningen (4)
- # luminus (20)
- # lumo (26)
- # off-topic (33)
- # onyx (27)
- # parinfer (1)
- # pedestal (3)
- # perun (5)
- # re-frame (20)
- # reagent (27)
- # ring (1)
- # ring-swagger (21)
- # shadow-cljs (259)
- # spacemacs (14)
- # yada (3)
@ikitommi with that ticket in particular, microbenchmarks on their own will be misleading, you will need to test across many differently sized vectors to get a result that will reflect real world use
@ikitommi what does your change do? Sometimes optimizations like this are simple and a clear win (Rich put one in for vector destructuring recently) while others can be really hard to debug due to callsites and how they work in the JVM
Thanks for the tips.
1) for keyword destructuring, change from (get m key)
to (key m)
=> enables fast access for records and is still nil safe (50ns -> 36ns for Records, no change for maps)
2) separate the generated inlined empty-sequence-check into a separate function, using a protocol dispatch (no MI here, should be fast all the time), takes 20ns of from both cases.
(defprotocol IntoMap
(into-map [x]))
(extend-protocol IntoMap
ISeq
(into-map [m]
(clojure.lang.PersistentHashMap/create (clojure.core/seq m)))
Object
(into-map [m] m))
Oh, but does the protocol really work? https://groups.google.com/forum/m/#!topic/clojure-dev/-zoLA78--Mo
On optimizations, it seems (from CLJ-1789) that using reduce instead of loop/recur with rest
is faster. Based on this, I tried out implementing some
in terms of reduce
, as such:
sure, but given that it’s faster in some cases and slower in others, it does not seem like an unambiguous win?
(defn my-every? [p coll]
(reduce (fn [a v] (if-not (p v) (reduced false) true)) true coll))
clj.user> (def r (range 10))
;; => #'clj.user/r
clj.user> (criterium.core/quick-bench (every? identity r))
Evaluation count : 4809738 in 6 samples of 801623 calls.
Execution time mean : 127.011808 ns
Execution time std-deviation : 5.742864 ns
Execution time lower quantile : 121.973369 ns ( 2.5%)
Execution time upper quantile : 133.507575 ns (97.5%)
Overhead used : 1.438648 ns
;; => nil
clj.user> (criterium.core/quick-bench (my-every? identity r))
Evaluation count : 8526336 in 6 samples of 1421056 calls.
Execution time mean : 72.917310 ns
Execution time std-deviation : 2.263637 ns
Execution time lower quantile : 70.634822 ns ( 2.5%)
Execution time upper quantile : 75.335391 ns (97.5%)
Overhead used : 1.438648 ns
@slipset some of the challenges are do to the way protocols are bootstrapped in core. Reduce isn't available until a bunch of things are loaded first
But could you move some/every? until after reduce and maybe declare them if they’re used before reduce?
playing core bootstrap Jenga is a fun way to pass the time :)
the technique taken in some cases is to start with a slow implementation, then reimplement with a fast one farther down in core
after the needed things are available
as in there would be more than one version of a function? And as more tools get defined you can change the implementation?
that’s what happens with reduce and reduce1 right?
well in that case there are two different functions
I was talking about the case where we literally reimplement the function
I can’t remember which one off the top of my head
maybe those are macros
stuff like let
is defined early (before destructuring) then redefined again later
so yall can use let in core before before defining all of the hairy destructuring parts?
yeah, just search for “redefine” in clojure/core.clj and you can find a few things like that
I asked this question a while back, but made the mistake of asking during the conj when everyone was busy. I didn’t get an unambiguous answer so I’ll try again: when direct linking is enabled, calls frequently appear twice in the stacktrace, one being the var invocation and one being the static method it delegates to. They frequently have different source line numbers, and the static one generally seems to be more accurate. Is that the one I should use for linking to that call?
I think so - I don't actually understand why they end up being tracked to different lines.
Assume it's something off in how the source line debug info is calculated in the two methods