Fork me on GitHub
#clojure
<
2021-11-16
>
emccue03:11:39

I’m seeing an unnerving difference between my AOT code and my non AOT code

emccue03:11:55

java.lang.IllegalStateException: ABORTED
	at org.eclipse.jetty.server.HttpChannelState.sendError(HttpChannelState.java:915)
	at org.eclipse.jetty.server.Response.sendError(Response.java:471)
	at ring.adapter.jetty9$proxy_handler$fn__85201.invoke(jetty9.clj:59)
	at ring.adapter.jetty9.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown Source)
	at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:59)
	at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:59)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)

emccue03:11:15

there are http2 errors that happen only if i am compiling a main and building into an uberjar

hiredman03:11:06

those are two orthogonal things, aot compiling and running from an uberjar

☝️ 1
hiredman03:11:27

build an uber jar without aot compiling and see if it still happens

emccue14:11:44

How can I do that? I don’t care that much about AOT, i just need the jar to run my main ns

hiredman03:11:18

my wild guess would be it is also happening when you run in the repl without aot compiling, the output is just being directed somewhere else so you aren't seeing it

emccue15:11:10

the other visible result of the error is that the server doesn’t accept any requests no matter what, so i don’t think it is happening in dev silently

hiredman03:11:06

(I am assuming when you say "non aot" code you mean running your code at dev time in the repl)

Santiago10:11:29

Does anyone recommend a Clojure/java lib to query WHOIS?

tvaughan11:11:24

Perhaps not the answer you're looking for, but you could just use http. For example, https://whoisjson.com/ I don't know anything about this particular service. This was just the top search result

vemv12:11:27

silly suggestion maybe, but you can always call dig over clojure.java.shell

1
noprompt18:11:34

Is clojure.test.check.random available as a separate library somewhere? I could’ve sworn it was but I’m starting to wonder if I just imagined it.

Alex Miller (Clojure team)18:11:57

doesn't ring a bell, but feel free to use it if you obey the license and preserve the copyright

noprompt18:11:16

Great. Thanks!

slipset20:11:42

On destructuring and speed: I see the following on My Machine(TM):

user> *clojure-version*
;; => {:major 1, :minor 10, :incremental 1, :qualifier nil}
user> (def m {:a 1})
;; => #'user/m
user> (criterium.core/quick-bench (let [{:keys [a]} m] a))
Evaluation count : 16846224 in 6 samples of 2807704 calls.
             Execution time mean : 28.819219 ns
    Execution time std-deviation : 0.805046 ns
   Execution time lower quantile : 27.750979 ns ( 2.5%)
   Execution time upper quantile : 29.760922 ns (97.5%)
                   Overhead used : 7.487563 ns
;; => nil
user> (criterium.core/quick-bench (let [a (get m :a)] a))
Evaluation count : 46207716 in 6 samples of 7701286 calls.
             Execution time mean : 5.958368 ns
    Execution time std-deviation : 0.436972 ns
   Execution time lower quantile : 5.618597 ns ( 2.5%)
   Execution time upper quantile : 6.663388 ns (97.5%)
                   Overhead used : 7.487563 ns

Found 1 outliers in 6 samples (16.6667 %)
	low-severe	 1 (16.6667 %)
ie, for this very small example on this clojure version, there almost a 5x penalty for using destructuring rather than a get. Are there any plans to address this (or have it been addressed in 1.11 with the new destructuring work that @fogus did), or should one just avoid destructuring whenever performance is critical?

ribelo21:11:37

try (m :a) 🙃

ribelo21:11:13

(m :a) is 50% faster than (:a m)

noprompt22:11:57

Nothing wrong with using get. If performance matters and destructuring costs you more than you want to pay, I think you have the answer to your question. 🙂

slipset06:11:45

Sure, but I tend to like destructuring, especially in fn params, and it would be nice if I could use it also in performance sensitive code.

Ben Sless06:11:09

You could always write your own clever version of destructure and hope for the best, but that's cooking with gas 🙂

Ben Sless06:11:11

It's also useful to know what's your performance budget. you can gain ~10-20 ns per key if you hand-tune your code. If your budget is under 1us, go for it, if it's 1ms, I wouldn't bother

hiredman21:11:40

for, uh, reasons, the same destructuring code is generated anywhere destructuring is used (fn paramters, let bindings, etc) and to support map rest parameters to functions, there is a extra check when map destructuring

hiredman21:11:13

so for example this works:

user=>  (def m (list :a 1))
#'user/m
user=> (let [{:keys [a]} m] a)
1
user=>

hiredman21:11:41

and the reason that works is because the code used to support

user=> ((fn [& {:keys [a]}] a) :a 1)
1
user=>
is also generated for let bindings where there is no rest

hiredman21:11:02

(rest, varargs, whatever)

hiredman21:11:41

so destructuring is doing more than just get, so it will be slower

slipset21:11:40

Yeah, I kinda understand that destructuring does more, but I would perhaps have hoped that since some of it happened at compile time, there was a possibility of analyzing the destruction and doing some smart stuff on the simpler cases?

Alex Miller (Clojure team)21:11:52

still a potential place for more work

Alex Miller (Clojure team)21:11:22

new stuff in 1.11 will not have changed this

Alex Miller (Clojure team)21:11:11

there were some interesting perf things explored in the process but I'm not sure any of that made it to the end