clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
2019-07-18T01:57:06.474400Z

Wow, I don't have enough evidence collected to confirm this hypothesis, but I might be seeing a case of JVM's JIT optimizing the method clojure.lang.Util.hasheq differently in two scenarios: (scenario 1) 99.9% of the calls to that method were with type Long (scenario 2) large fraction of calls were with type long, but also a large fraction with type PersistentHashSet. In scenario 2, the clojure.lang.Util.hasheq calls on type long are much much slower.

2019-07-18T01:57:56.475400Z

If that hypothesis is true, then I am awestruck with the extra levels of complexity of predicting the performance of code running on the JVM.

alexmiller 2019-07-18T02:34:40.476400Z

without knowing more that sounds like could be difference between monomorphic and bimorphic inlining

alexmiller 2019-07-18T02:35:15.476700Z

which version of java are you on?

2019-07-18T02:37:23.478300Z

These measurements are with OpenJDK 11.0.3 installed via apt-get on Ubuntu 18.04 Linux. I don't have clj-async-profiler measurements on macOS running Oracle/Apple JDK 1.8.0_192 but am seeing similar overall elapsed times of the two scenarios there.

alexmiller 2019-07-18T02:37:53.478800Z

If you're really interested, check out Aleksey Shipilev's blogs like https://shipilev.net/jvm/anatomy-quarks/16-megamorphic-virtual-calls/

alexmiller 2019-07-18T02:38:12.479300Z

you can pass jvm options to see what the jvm is doing around a lot of these kinds of things

2019-07-18T02:40:20.479700Z

Thanks for the link. I will at least start down the rabbit hole, but not sure how far I want to go just yet.

alexmiller 2019-07-18T02:43:29.480500Z

it's normal for bimorphic or megamorphic things to not inline as well so what you said above does not seem surprising to me

alexmiller 2019-07-18T02:44:37.481600Z

Clojure protocol call-site caches have the same kind of potential issue - last choice is cached, so monomorphic case will perform much better

seancorfield 2019-07-18T04:48:17.484600Z

Over the years I've worked with Clojure, I've often found that I've wanted to work with data -- hash maps -- but also work with Java objects that have setters. An example I'm working with right now is HikariCP and there's a Clojure wrapper that supports Clojure hash maps and turns them into .setFooBar calls for :foo-bar members. I want to do this in general and it feels like this ought to be part of core. Do we already have something that I'm just missing? Is this something that might be considered for core?

2019-07-18T05:09:23.485900Z

Java.data has something like that

seancorfield 2019-07-18T05:10:20.486400Z

'k thx...

alexmiller 2019-07-18T05:12:50.487Z

I've written macros in limited domains to do this kind of thing in the past, as always depends how generic and/or customizable you want to make it

seancorfield 2019-07-18T05:37:31.488Z

@hiredman It's close. :fooBar is needed instead of :foo-bar but it's close enough that I can use it for what I'm trying to do. Thanks.

alexmiller 2019-07-18T05:39:17.488400Z

well, that's the customizability - can overload the multimethod to fix

seancorfield 2019-07-18T05:44:33.488700Z

It's perfect for what I need, to be honest.

seancorfield 2019-07-18T06:19:29.489800Z

Turns out that org.clojure/java.data does exactly what I need for next.jdbc to use c3p0 and HikariCP so I can bake that into the library with minimal effort.

2019-07-18T19:14:58.491100Z

My performance problem sanity is restored -- I should have been looking for a simpler explanation, which was that clojure.lang.Util/hasheq was being called so many more times in my 10x worse performance scenario, about 10x more times, in fact. Duh. Application code that was the cause found and fixed.

alexmiller 2019-07-18T19:26:24.491400Z

Cool :)

alexmiller 2019-07-18T19:26:42.492Z

Calling things more times is usually slower

2019-07-18T19:45:03.493600Z

It was not the cause of any problems for me, but was curious if you thought there may be any interest in a CLJ JIRA performance-related ticket on the equiv() methods I found that lack an identical fast path?

alexmiller 2019-07-18T19:45:53.494100Z

sure, if you happened to find a path where that's not used and makes a demonstrable difference

2019-07-18T19:46:54.495200Z

I can add a scenario to the ticket where it can show up -- using maps or sets as keys in array-map's, and the maps/sets you look up or assoc on are identical to the ones added earlier.

2019-07-18T19:48:08.495800Z

also, the equiv() methods that lack those identical fast paths are probably over typical inlining code size limits, I would guess, but can investigate that further, later.

bronsa 2019-07-18T20:14:01.496400Z

there's a few in the patch for https://clojure.atlassian.net/browse/CLJ-1792

2019-07-18T20:54:33.496700Z

Hacky, crappy test

2019-07-18T20:54:36.497100Z

seems to show in profiling

2019-07-18T20:54:57.497400Z

(I would write something better for an “official” Jira thing…)

2019-07-18T20:57:40.497900Z

on the long case - with array-map, I see the all the action being in the equiv() call

2019-07-18T22:46:50.499400Z

Regarding method/function call counts on the JVM, I know that JVMs often make decisions on which methods to JIT based upon call counts, at least as one of the deciding factors. Do Flight Recorder and/or other perf tools make those call counts visible, without having to modify the code?

souenzzo 2019-07-18T22:59:17.499500Z

playing with NaN

(= ##NaN ##NaN)
=> false
(not= ##NaN ##NaN)
=> false
(apply = [##NaN ##NaN])
=> false
(apply not= [##NaN ##NaN])
=> true

alexmiller 2019-07-18T23:18:25.499800Z

Yes

alexmiller 2019-07-18T23:18:45.000300Z

The jvm itself can do some of that