Fork me on GitHub

Weird performance fact -- I am so used to thinking of transients as faster than their persistent counterparts, that I was mystified when writing what I thought were performance-improved versions of clojure.set/union. If you do the persistent (reduce conj s1 s2) at the heart of clojure.set/union, where s2 is a subset of s1, then it is a little bit faster than doing (-> (reduce conj! (transient s1) s2) persistent!) in that case, and the difference gets larger with larger sets -- about 15 to 20% slower for the transient version with Clojure 1.9.0 and some Oracle/Apple version of JDK 1.8.0 in testing on one machine.


This is for the normal PersistentHashSet class for s1 and s2. If s2 is disjoint from s1, then the transient version is significantly faster.


Yes, I think the thing we overlook sometimes is that saying transient conversion ops are O(1) doesn’t mean that they’re cheap or free. If they are the same cost as a call to conj, then you’d need at least 2-3 modifying operations to make transients worth the cost.


I know they are O(1), but the weird thing here is that it seems that the individual conj! calls on a transient version of a PersistentHashSet are more expensive than the corresponding conj calls on a PersistentHashSet, when the element is already in the set.


volatile accesses in transients may be the culprit


Has anyone poked at making vars and namespaces participate in the ref transaction system? It can't get you to fully revertable code loading even if you integrated DynamicClassLoader, but it seems like it'd get you a long way.

😎 1