Tim,

Every time you call @anon, it creates a brand-new type (and an instance of 
> that type) that julia has never seen before.


It looks like my understanding of FastAnonymous was flawed. Why doesn't it 
create the type at macro time, and just instantiate it at runtime, yielding 
1 type / @anon ? Is there any complication that prevents that?

Cédric

On Friday, January 22, 2016 at 2:15:20 PM UTC-5, Tim Holy wrote:
>
> On Friday, January 22, 2016 10:21:31 AM Bryan Rivera wrote: 
> > For 1000 elements: 
> > 
> > 0.00019s vs 0.035s respectively 
> > 
> > Thanks! 
>
> Glad it helped. 
>
> > 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.) 
>
> Every time you call @anon, it creates a brand-new type (and an instance of 
> that type) that julia has never seen before. That requires JITting any 
> code 
> that gets invoked on this instance. So the usual advice, "run once to JIT, 
> then do your timing" doesn't work in this case: it JITs every time. 
>
> --Tim 
>
> > 
> > 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