Yea those were averages of 5 runs each, minus the first for JIT.

On Friday, January 22, 2016 at 1:24:00 PM UTC-5, Stefan Karpinski wrote:
>
> Make sure you time it twice – the faster version may generate more code.
>
> On Fri, Jan 22, 2016 at 1:21 PM, Bryan Rivera <future...@gmail.com 
> <javascript:>> wrote:
>
>> dude..
>>
>> dictZ = Dict{Int, Int}()
>>
>> for z = 1:1000
>>     dictZ[z] = z
>> end
>>
>> function testNoFunCopy()
>>   for z = 1:1000
>>       function2.z = dictZ[z]
>>       # do whatever with function2, including making a copy
>>   end
>> end
>>
>> @code_llvm testNoFunCopy()
>> @code_native testNoFunCopy()
>>
>> @time testNoFunCopy()
>>
>> # Test to see if multiple functions are created.  They are.
>> # We would only need to create a single function if we used julia anon, 
>> but its time inefficient.
>>
>> dict = Dict{Int, Any}()
>>
>> for z = 1:1000
>>     function2 = @anon c -> (c + z)
>>     dict[z] =  function2
>> end
>>
>> a = 1
>> b = 2
>>
>> function testWithFunCopy()
>>   for z = 1:1000
>>     function1(a,b, dict[z])
>>   end
>> end
>>
>>
>> @code_llvm testWithFunCopy()
>> @code_native testWithFunCopy()
>>
>> @time testWithFunCopy()
>>
>>
>> For 1000 elements:   
>>
>> 0.00019s vs 0.035s respectively
>>
>> Thanks!
>>
>> Is the reason the faster code has more allocations bc it is 
>> inserting vars into the single function?  (Opposed to the slower 
>> code already having its vars filled in.)
>>
>>
>> On Friday, January 22, 2016 at 12:23:59 PM UTC-5, Tim Holy wrote:
>>>
>>> Just use 
>>>
>>> z = 1 
>>> function2 = @anon c -> c + z 
>>> for z = 1:100 
>>>     function2.z = z 
>>>     # do whatever with function2, including making a copy 
>>> end 
>>>
>>> --Tim 
>>>
>>> On Friday, January 22, 2016 08:55:25 AM Cedric St-Jean wrote: 
>>> > (non-mutating) Closures and FastAnonymous work essentially the same 
>>> way. 
>>> > They store the data that is closed over (more or less) and a function 
>>> > pointer. The thing is that there's only one data structure in Julia 
>>> for all 
>>> > regular anonymous functions, whereas FastAnonymous creates one per 
>>> @anon 
>>> > site. Because the FastAnonymous-created datatype is specific to that 
>>> > function definition, the standard Julia machinery takes over and 
>>> produces 
>>> > efficient code. It's just as good as if the function had been defined 
>>> > normally with `function foo(...) ... end` 
>>> > 
>>> > 
>>> > for z = 1:100 
>>> >     function2 = @anon c -> (c + z) 
>>> > 
>>> >     dict[z] =  function2 
>>> > end 
>>> > 
>>> > 
>>> > So we end up creating multiple functions for each z value. 
>>> > 
>>> > 
>>> > In this code, whether you use @anon or not, Julia will create 100 
>>> object 
>>> > instances to store the z values. 
>>> > 
>>> > The speed difference between the two will soon be gone. 
>>> > <https://github.com/JuliaLang/julia/pull/13412> 
>>> > 
>>> > Cédric 
>>> > 
>>> > On Friday, January 22, 2016 at 11:31:36 AM UTC-5, Bryan Rivera wrote: 
>>> > > I have to do some investigating here.  I thought we could do 
>>> something 
>>> > > like that but wasn't quite sure how it would look. 
>>> > > 
>>> > > Check this out: 
>>> > > 
>>> > > This code using FastAnonymous optimizes to the very same code below 
>>> it 
>>> > > where functions have been manually injected: 
>>> > > 
>>> > > using FastAnonymous 
>>> > > 
>>> > > 
>>> > > function function1(a, b, function2) 
>>> > > 
>>> > >   if(a > b) 
>>> > >   
>>> > >     c = a + b 
>>> > >     return function2(c) 
>>> > >   
>>> > >   else 
>>> > >   
>>> > >     # do anything 
>>> > >     # but return nothing 
>>> > >   
>>> > >   end 
>>> > > 
>>> > > end 
>>> > > 
>>> > > 
>>> > > z = 10 
>>> > > function2 = @anon c -> (c + z) 
>>> > > 
>>> > > 
>>> > > a = 1 
>>> > > b = 2 
>>> > > @code_llvm function1(a, b, function2) 
>>> > > @code_native function1(a, b, function2) 
>>> > > 
>>> > > Manually unrolled equivalent: 
>>> > > 
>>> > > function function1(a, b, z) 
>>> > > 
>>> > >   if(a > b) 
>>> > >   
>>> > >     c = a + b 
>>> > >     return function2(c, z) 
>>> > >   
>>> > >   else 
>>> > >   
>>> > >     # do anything 
>>> > >     # but return nothing 
>>> > >   
>>> > >   end 
>>> > > 
>>> > > end 
>>> > > 
>>> > > 
>>> > > function function2(c, z) 
>>> > > 
>>> > >   return c + z 
>>> > > 
>>> > > end 
>>> > > 
>>> > > 
>>> > > a = 1 
>>> > > b = 2 
>>> > > z = 10 
>>> > > 
>>> > > 
>>> > > @code_llvm function1(a, b, z) 
>>> > > 
>>> > > @code_native function1(a, b, z) 
>>> > > 
>>> > > However, this is a bit too simplistic.  My program actually does 
>>> this: 
>>> > > 
>>> > > # Test to see if multiple functions are created.  They are. 
>>> > > # We would only need to create a single function if we used julia 
>>> anon, 
>>> > > but its time inefficient. 
>>> > > 
>>> > > dict = Dict{Int, Any}() 
>>> > > for z = 1:100 
>>> > > 
>>> > >     function2 = @anon c -> (c + z) 
>>> > >     
>>> > >     dict[z] =  function2 
>>> > > 
>>> > > end 
>>> > > 
>>> > > 
>>> > > a = 1 
>>> > > b = 2 
>>> > > 
>>> > > function test() 
>>> > > 
>>> > >   function1(a,b, dict[100]) 
>>> > >   function1(a,b, dict[50]) 
>>> > > 
>>> > > end 
>>> > > 
>>> > > @code_llvm test() 
>>> > > @code_native test() 
>>> > > 
>>> > > 
>>> > > 
>>> > > So we end up creating multiple functions for each z value.  We could 
>>> use 
>>> > > Julia's anon funs, which would only create a single function, 
>>> however 
>>> > > these 
>>> > > lamdas are less performant than FastAnon. 
>>> > > 
>>> > > So its a space vs time tradeoff, I want the speed of FastAnon, 
>>> without the 
>>> > > spacial overhead of storing multiple functions. 
>>> > > 
>>> > > Can we be greedy?  :) 
>>> > > 
>>> > > On Thursday, January 21, 2016 at 9:56:51 PM UTC-5, Cedric St-Jean 
>>> wrote: 
>>> > >> Something like this? 
>>> > >> 
>>> > >> function function1(a, b, f) # Variable needed in callback fun 
>>> injected. 
>>> > >> 
>>> > >>     if(a > b) 
>>> > >>     
>>> > >>       c = a + b 
>>> > >>       res = f(c) # Callback function has been injected. 
>>> > >>       return res + 1 
>>> > >>     
>>> > >>     else 
>>> > >>     
>>> > >>       # do anything 
>>> > >>       # but return nothing 
>>> > >>     
>>> > >>     end 
>>> > >> 
>>> > >> end 
>>> > >> 
>>> > >> type SomeCallBack 
>>> > >> 
>>> > >>     z::Int 
>>> > >> 
>>> > >> end 
>>> > >> Base.call(callback::SomeCallBack, c) = c + callback.z 
>>> > >> 
>>> > >> function1(2, 1, SomeCallBack(10)) 
>>> > >> 
>>> > >> Because of JIT, this is 100% equivalent to your "callback function 
>>> has 
>>> > >> been injected" example, performance-wise. My feeling is that .call 
>>> > >> overloading is not to be abused in Julia, so I would favor using a 
>>> > >> regular 
>>> > >> function call with a descriptive name instead of call overloading, 
>>> but 
>>> > >> the 
>>> > >> same performance guarantees apply. Does that answer your question? 
>>> > >> 
>>> > >> On Thursday, January 21, 2016 at 9:02:50 PM UTC-5, Bryan Rivera 
>>> wrote: 
>>> > >>> I think what I wrote above might be too complicated, as it is an 
>>> attempt 
>>> > >>> to solve this problem. 
>>> > >>> 
>>> > >>> In essence this is what I want: 
>>> > >>> 
>>> > >>> 
>>> > >>> # wasmerged, _, _, _ = elide_pairwise!(ttree1, ttree2, canmerge; 
>>> nbrs=idbgv) 
>>>
>>> > >>> function function1(a, b, onGreaterThanCallback) 
>>> > >>> 
>>> > >>>   if(a > b) 
>>> > >>>   
>>> > >>>     c = a + b 
>>> > >>>     res = onGreaterThanCallback(c, z) 
>>> > >>>     return res + 1 
>>> > >>>   
>>> > >>>   else 
>>> > >>>   
>>> > >>>     # do anything 
>>> > >>>     # but return nothing 
>>> > >>>   
>>> > >>>   end 
>>> > >>> 
>>> > >>> end 
>>> > >>> 
>>> > >>> 
>>> > >>> global onGreaterThanCallback = (c) -> c + z 
>>> > >>> 
>>> > >>> function1(a, b, onGreaterThanCallback) 
>>> > >>> 
>>> > >>> 
>>> > >>> Problems: 
>>> > >>> 
>>> > >>> The global variable. 
>>> > >>> 
>>> > >>> The anonymous function which has performance impact (vs other 
>>> > >>> approaches).  We could use Tim Holy's @anon, but then the value of 
>>> `z` 
>>> > >>> is 
>>> > >>> fixed at function definition, which we don't always want. 
>>> > >>> 
>>> > >>> I think that the ideal optimization would look like this: 
>>> > >>>       function function1(a, b, z) # Variable needed in callback 
>>> fun 
>>> > >>> 
>>> > >>> injected. 
>>> > >>> 
>>> > >>>         if(a > b) 
>>> > >>>         
>>> > >>>           c = a + b 
>>> > >>>           res = c + z # Callback function has been injected. 
>>> > >>>           return res + 1 
>>> > >>>         
>>> > >>>         else 
>>> > >>>         
>>> > >>>           # do anything 
>>> > >>>           # but return nothing 
>>> > >>>         
>>> > >>>         end 
>>> > >>>       
>>> > >>>       end 
>>> > >>>       
>>> > >>>       
>>> > >>>       function1(a, b, z) 
>>> > >>> 
>>> > >>> In OO languages we would be using an abstract class or its 
>>> equivalent. 
>>> > >>> 
>>> > >>>  But I've thought about it, and read the discussions on 
>>> interfaces, and 
>>> > >>> 
>>> > >>> don't see those solutions optimizing the code out like I did 
>>> above. 
>>> > >>> 
>>> > >>> Any ideas? 
>>>
>>>
>

Reply via email to