Fork me on GitHub
#clojure-dev
<
2018-08-30
>
gfredericks12:08:20

it is weird that the compiler seems to store symbols in clojure.lang.AFn fields; does anybody know if that serves a purpose?

gfredericks12:08:47

it's my observation when javap -cing some generated class files

alexmiller12:08:31

Closed over vals get stored in fields

alexmiller12:08:45

Is that what you’re seeing?

gfredericks12:08:50

this is a literal used in the function

gfredericks12:08:58

so it's stored in static init {}

gfredericks12:08:00

I annotated the fields; const__4 is an AFn for storing a symbol

gfredericks12:08:23

I see that in another class as well

gfredericks12:08:04

I doubt it causes any problems, it's just weird

gfredericks12:08:23

"sometimes the clojure compiler is weird" is a perfectly good answer 🙂

gfredericks12:08:58

(to be clear, I would have expected either of clojure.lang.Symbol or Object)

gfredericks12:08:43

this whole class just represents the call (commute @#'*loaded-libs* conj 'user.empty-ns)

alexmiller12:08:55

Oh, that’s just filling the constant pool to set up static initialization

gfredericks12:08:38

sure but why AFn for a symbol?

bronsa12:08:22

@gfredericks because the compiler emits either Var/AFn/String/Keyword or Object, and a symbol happens to be an AFn

bronsa12:08:56

as to why it limits itself to that list of classes god knows

bronsa12:08:10

since the emitted bytecode has checkcasts anyway

gfredericks12:08:56

Could some checkcasts be eliminated for these fields?

bronsa12:08:13

quite a bit of checkcasts that the clojure compiler emits could be avoided

bronsa12:08:33

hardly a priority

gfredericks12:08:16

Maybe the jvm does a good job of optimizing them?

bronsa12:08:13

checkcasts are close to free ops

gfredericks12:08:19

Okay cool well anyhow that "explains" the AFn thing well enough

ghadi14:08:44

@bronsa they're actually free in hot code where there is runtime type profiling. They are not free inside <clinit>, which is by definition always cold

bronsa14:08:54

:thumbsup:

bronsa14:08:31

I can't imagine the amount of checkcasts in <clinit> causing performance issues tho, but I've never profiled it

ghadi14:08:29

insignificant contribution to startup time

ghadi14:08:31

getting the Symbol.intern() and RT.var() calls out of <clinit> entirely would be a much larger payoff

gfredericks19:08:01

how would you get them out? do them lazily the first time?

ghadi20:08:55

Constant Dynamic (JDK11)

ghadi20:08:03

or Indy (Java 7+)

gfredericks20:08:15

ah right yeah cool

mikerod15:08:30

@ghadi > they’re actually free in hot code where there is runtime type profiling Is this based on some callsite caching? Can it be costly if the site is megamorphic etc?

mikerod15:08:43

Or perhaps that doesn’t apply here. Just curious. I’ve often wondered what the overhead of checkcast may be.

ghadi15:08:29

@mikerod if you have an AFn on the stack, and it checkcasts to IFn, that can be completely eliminated if you have Object on the stack, and then a checkcast to IFn, the JIT can profile and specialize -- then do on-stack replacement if the assumptions don't work out

mikerod15:08:57

Ok, that makes sense.

alexmiller18:08:26

I have run into cases in hot numeric loops where there was a measurable difference that seemed attributable to checkcast (but this experience probably dates to jdk 6 or maybe 7 era)

alexmiller18:08:28

This was comparing bytecode generated by Java vs Clojure which was nearly identical

mikerod20:08:13

Interesting. Sounds like it typically isn’t a big overhead though I guess.

mikerod20:08:39

I guess if there are too many checkcast all over though, it adds bytecodes which increases method size, which eventually can affect inlining stuff I guess

mikerod20:08:55

but probably not enough of them to weigh in much on that perhaps

alexmiller21:08:04

yeah, so much optimization goes into each jvm version though that who knows if I could replicate that today

1