On Friday, April 24, 2015 at 8:11:52 PM UTC+10, Mauro wrote:
>
> >> Well it seems Julia should know that nothing is used from fn!, without 
> >> knowing anything about fn!.  That is at least what @code_warntype 
> >> suggest (with julia --inline=no).  For 
> >> 
> >> function f(ar) 
> >>     for i=1:n 
> >>         hh!(ar, i) 
> >>     end 
> >> end 
> >> 
> >> the loop gives: 
> >> 
> >>       GenSym(1) = $(Expr(:call1, :(top(next)), GenSym(0), 
> :(#s1::Int64))) 
> >>       i = $(Expr(:call1, :(top(getfield)), GenSym(1), 1)) 
> >>       #s1 = $(Expr(:call1, :(top(getfield)), GenSym(1), 2)) # line 71: 
> >>       $(Expr(:call1, :hh!, :(ar::Array{Float64,1}), :(i::Int64))) 
> >>       3: 
> >>       unless $(Expr(:call1, :(top(!)), :($(Expr(:call1, :(top(!)), 
> >> :($(Expr(:call1, :(top(done)), GenSym(0), :(#s1::Int64))))))))) goto 2 
> >> 
> >> For 
> >> function g(fn!,ar) 
> >>     a = 0 
> >>     for i=1:n 
> >>         fn!(ar, i) 
> >>     end 
> >>     a 
> >> end 
> >> 
> >> the loop gives: 
> >>       2: 
> >>       GenSym(1) = $(Expr(:call1, :(top(next)), GenSym(0), 
> :(#s1::Int64))) 
> >>       i = $(Expr(:call1, :(top(getfield)), GenSym(1), 1)) 
> >>       #s1 = $(Expr(:call1, :(top(getfield)), GenSym(1), 2)) # line 78: 
> >>       (fn!::F)(ar::Array{Float64,1},i::Int64)::Any 
> >> 
> > 
> > Wouldn't it (or fn!) need to allocate for this Any here ^ 
> > 
> > IIUC its fn! that decides if it returns something, and even if the 
> caller 
> > doesn't need it, the return value still has to be stored somewhere. 
>
> I think this optimisation should work irrespective of what fn! returns 
> by the fact that the value is not used.  This and more seems to happen 
> in the first-order function.  Here a version of first-order 
> function which calls a function which returns an inferred Any: 
>
> const aa = Any[1] 
> hh_ret!(ar,i) = (ar[i] = hh(ar[i]); aa[1]) 
>
> function f_ret(ar) 
>     a = aa[1] 
>     for i=1:n 
>         a=hh_ret!(ar, i) 
>     end 
>     a 
> end 
> julia> @time f_ret(a); 
> elapsed time: 0.259893608 seconds (160 bytes allocated) 
>
> It's still fast and doesn't allocate, even though it uses the value! 
>

The a= aa[1] is unused and can be removed (well if n is >=1).

Then I would have thought that first hh_ret() is inlined, then the loop 
body is visible to the optimiser and the unused value is removed as it is 
optimised to ar[n].

But if hh_ret() was passed to f_ret() it can only be inlined if f_ret() is 
re-compiled for each call with a new parameter function.  Thats not 
impossible in a dynamic language like Julia.  But still may not work if the 
function being passed as a parameter is the result of an expression that 
can't be resolved at compile time.

As Tim says its all optimisations that havn't yet been written :)

 

>
> > Maybe fn! does the allocating, but it still happens. 
>
> It's a different story if there is actual allocation in fn!. 
>
> >>       3: 
> >>       unless $(Expr(:call1, :(top(!)), :($(Expr(:call1, :(top(!)), 
> >> :($(Expr(:call1, :(top(done)), GenSym(0), :(#s1::Int64))))))))) goto 2 
> >> 
> >> So, at least from my naive viewpoint, it looks like there is no need 
> for 
> >> an allocation in this case.  Or is there ever a case when the return 
> >> value of fn! would be used? 
> >> 
> >> I think this is quite different from the case when the return value of 
> >> fn! is used because then, as long as Julia cannot do type inference on 
> >> the value of fn!, it cannot know what the type is. 
> >> 
> > 
> > Which is why its ::Any above I guess and my understanding is Any means 
> > boxed and allocated on the heap? 
>
> Thanks for indulging me! 
>

Reply via email to