You're going down a rabbit hole here. Evaluating forms at runtime will 
always result in a slower execution time than a function that doesn't 
evaluate a form at runtime. 

On Thursday, July 3, 2014 11:55:02 AM UTC-4, Pascal Germroth wrote:
>
> Hi Atamert,
>
> Here's a gist with the example code: 
> https://gist.github.com/neapel/4e502a14e3738b709672
>
> I tried replacing a closure with a dynamically built and evaluated 
>>> metafunction but discovered that it was actually slower.
>>>
>>
>> If evaluating code during run is slower than AOT compiling it, it 
>> wouldn't surprise me.
>>
>
> I didn't actually try AOT compiling it, just using the REPL. As far as I 
> understand there shouldn't be a difference in speed, since (eval) runs the 
> same compiler as AOT does, and even writes out a .class file.
>
> (possibly unrelated, maybe due to eval, when trying to AOT compile the 
> example project by adding `:aot :all` and running `lein jar`, it appears to 
> fall into an infinite loop. The JAR file grew to 8GB, but I couldn't list 
> its contents.)
>  
>
>> I guess f2 runs this much slower than f1 because it doesn't actually pass 
>>> a reference to x when unquoting, but clones the value, which means it needs 
>>> to compare the lists element-wise.
>>>
>>
>> This is interesting. Were you able to confirm it or are you just 
>> guessing? (I'm just curious.) 
>>
>
>  The code in the Gist produces a nice table now, here's my result (I 
> couldn't reproduce the case where f3 runs as fast as f1 for vectors; but 
> it's actually less weird this way)
> I think it's quite obvious that f1 compares always references, as does the 
> "nil" case, while the "vec" and "map" cases compare by value:
>
> |    map |    vec |  nil | name |
> |--------+--------+------+------|
> |   5 ns |   5 ns | 5 ns |   f1 |
> | 558 ns | 134 ns | 6 ns |   f2 |
> | 567 ns | 130 ns | 7 ns |   f3 |
> | 561 ns | 117 ns | 7 ns |   f4 |
>
> It also dumps the disassembly, we can see that:
>
> f1-map == f1-vec == f1-nil (makes sense, as the reference to the value is 
> passed in the constructor of the closure)
> f2-map = f3-map, f2-vec = f3-vec, f2-nil = f3-nil (with renaming)
> f4-map = f1, f4-vec = f1, f4-nil = f1 (with renaming)
> f2-map = f2-vec, except for calling clojure.lang.RT.map / vector.
>
> So there are only three versions of the actual bytecode:
> - the closure f1/f4;
> - f2 building a new object in its static constructor
> - f2-nil using a null constant instead of a field containing null.
>
> importantly, f1/f4 are identical:
> public final class F extends clojure.lang.AFunction {
>   /* ... static constructor getting clojure.core/= ... */
>   Object x;
>   public F(Object o) { x = o }
>   public Object invoke( Object y ) {
>     /*basically*/ return (bool (= x y))
>   }
> }
>
> So why does f4 behave almost like f2/f3?!
>
>
> Cheers,
>
> -- 
> pascal
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to