This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
Is there a clean way to have a macro produce a namespace-qualified symbol? The following works, but it feels a bit convoluted:
(defmacro ns-qualify [x] `(symbol (str ~*ns*) ~(name x)))
(ns-qualify foo) ; => my.ns/foo
Hmm.. I actually realized this doesn't seem to work in CLJS.
It's giving me clojure.lang.Namespace is not a valid ClojureScript constant.
(defmacro ns-qualify [x] `(symbol ~(str *ns*) ~(name x)))
does work thoughAnyway, thanks. I figured there might be some sort of magic quoting trick I could do to have the compiler do this work instead of me, but perhaps not.
I'm also curious why this needs to be a macro.
*ns*
is not a macro, but it doesn't exist in CLJS outside of macros or bootstrapped environments.
I have a map, m
. I need to dissoc
6 fields from it and update
4 other fields on it. If I thread the map through these 10 forms, I’ll potentially end up with a bunch of garbage for GC. I have several different ideas of how to clean this up, but I’m looking for suggestions. How would you structure this code?
> I’ll potentially end up with a bunch of garbage for GC Is this truly a problem that you have observed and measured to be of a significant impact?
> dissoc
accepts multiple keys.
In my experience, criterium shows that route to be slower than just calling dissoc multiple times, but I know you and I have run into differing results from criterium in the past.
>> I’ll potentially end up with a bunch of garbage for GC > Is this truly a problem that you have observed and measured to be of a significant impact? No, but this is just a side project where that part doesn’t really matter. I’m just using this as practice for when I build real applications where those implications are important. This piece of code that I’m optimizing is a “hot-spot” that is called many times in a short period of time. However, in the grand scheme of things, a few millis of performance difference won’t matter for this application.
Could select-keys
instead of dissoc
. Or even do the whole thing inside a (rather imperative) reduce-kv
.
I considered select-keys
except this function doesn’t know all of the keys that might be in m
. It just knows that there’s certain keys it wants to remove, and certain ones it wants to update.
I gave reduce-kv
a shot, but that was harder to read and slower than the alternatives
Seems like turning the 6 calls to dissoc
into a single call to dissoc
is the best way to go
Hey, wanna cook with gas?
(alter-meta! #'clojure.core/dissoc assoc :inline
(fn
([m] m)
([m k] `(. clojure.lang.RT (dissoc ~m ~k)))
([m k & ks] `(-> ~m ~@(map (fn [k] `(dissoc ~k)) (cons k ks))))))
Yes transient is the answer here that's why they exist, for exactly these kind of use cases where you need to add or remove many elements at a time.
You still pay a pretty high cost for iteration, so dissoc should still be unrolled. It also generates garbage to allocate and iterate over the args
Oh yeah, I profiled that area to death. Maybe if it were array iteration it could have, but doing it with first/rest? Impossible
In this case its just threading through right? Do you know if threading and/or loop/recur can be optimized by the JIT
Oh, are you talking about the dissoc that takes more than one arg? Does it use sequences to do so under the hood?
Yes. The rest args get packed in an array and it gets packed in a list facade. Iterating over it with first and rest returns a new *cursor" like object
Side note, I'd also be curious about transducers. Basically, what loop is best for JIT unrolling?
Thought the core function had that inline code. Ok, ya I see why that arity is actually slower than just making repeated calls to disocc. I never used that arity to be honest, didn't even realize it was there.
FWIW I profiled an option that uses reduce-kv
with transient!
but my results showed it to be slower than the other options
I've found that conversion to and back from transient adds a bit of overhead, so you want to use it when you have a lot of insert/remove. In your case, you have like 15 only, which isn't a lot of insert/remove, so maybe the transient conversion overhead makes it actually slower
I also found loop/recur is always the fastest way to loop. And I mean unrolling like Ben is saying would be even faster.
But to me these are two distinct problems. How fast can you iterate and execute a function repeatedly is one dimension. Unrolling will be fastest, followed by loop/recur, then iterator based loops like reduce or transducers, then sequence loops. And the other question is how do you insert a new key/value or remove one from a map most quickly. This will be faster on a transient map, but conversion from/back transient and persistent adds a fixed overhead. So for inserting a single thing it won't be faster probably, but if you're doing a lot of them inside a loop, it will pay off.
My conclusion here is Ben is right (he's the expert at micro-opromizations afterall). If you know the set of keys, and it's under 100 (that's kind of a guess), unrolling and using normal assoc/dissoc is probably fastest. If you don't know the set of keys, I'd assume loop/recur would be best, and then if you'll have like 100+ (again just a guess) probably using transient will be fastest.
Also, FYI, when using loop/recur you need to do so with iterator as well, if you use first/next/rest with it you're back at using sequences. loop/recur just avoids the function overhead for the body caused by reduce.