Nice video recommendation, Yichao. Thanks.

On Saturday, April 2, 2016 at 1:16:07 PM UTC-3, Yichao Yu wrote:
>
> On Sat, Apr 2, 2016 at 10:26 AM, Cedric St-Jean <[email protected] 
> <javascript:>> wrote: 
> > 
> >> Therefore there's no way the compiler can rewrite the slow version to 
> the 
> >> fast version. 
> > 
> > 
> > It knows that the element type is a Feature, so it could produce: 
> > 
> > if isa(features[i], A) 
> >     retval += evaluate(features[i]::A) 
> > elseif isa(features[i], B) 
> >     retval += evaluate(features[i]::B) 
> > else 
> >     retval += evaluate(features[i]) 
> > end 
>
> This is kind of the optimization I mentioned but no this will still be 
> much slower than the other version. 
> The compiler has no idea what the return type of the third one so this 
> version is still type unstable and you get dynamic dispatch at every 
> iteration for the floating point add. Of course there's more 
> sophisticated transformation that can keep you in the fast path as 
> long as possible and create extra code to check and handle the slow 
> cases but it will still be slower. 
>
> I also recommand Jeff's talk[1] for a better explaination of the general 
> idea. 
>
> [1] https://www.youtube.com/watch?v=cjzcYM9YhwA 
>
> > 
> > and it would make sense for abstract types that have few subtypes. I 
> didn't 
> > realize that dispatch was an order of magnitude slower than type 
> checking. 
> > It's easy enough to write a macro generating this expansion, too. 
> > 
> > On Saturday, April 2, 2016 at 2:05:20 AM UTC-4, Yichao Yu wrote: 
> >> 
> >> On Fri, Apr 1, 2016 at 9:56 PM, Tim Wheeler <[email protected]> 
> wrote: 
> >> > Hello Julia Users. 
> >> > 
> >> > I ran into a weird slowdown issue and reproduced a minimal working 
> >> > example. 
> >> > Maybe someone can help shed some light. 
> >> > 
> >> > abstract Feature 
> >> > 
> >> > type A <: Feature end 
> >> > evaluate(f::A) = 1.0 
> >> > 
> >> > type B <: Feature end 
> >> > evaluate(f::B) = 0.0 
> >> > 
> >> > function slow(features::Vector{Feature}) 
> >> >     retval = 0.0 
> >> >     for i in 1 : length(features) 
> >> >         retval += evaluate(features[i]) 
> >> >     end 
> >> >     retval 
> >> > end 
> >> > 
> >> > function fast(features::Vector{Feature}) 
> >> >     retval = 0.0 
> >> >     for i in 1 : length(features) 
> >> >         if isa(features[i], A) 
> >> >             retval += evaluate(features[i]::A) 
> >> >         else 
> >> >             retval += evaluate(features[i]::B) 
> >> >         end 
> >> >     end 
> >> >     retval 
> >> > end 
> >> > 
> >> > using ProfileView 
> >> > 
> >> > features = Feature[] 
> >> > for i in 1 : 10000 
> >> >     push!(features, A()) 
> >> > end 
> >> > 
> >> > slow(features) 
> >> > @time slow(features) 
> >> > fast(features) 
> >> > @time fast(features) 
> >> > 
> >> > The output is: 
> >> > 
> >> > 0.000136 seconds (10.15 k allocations: 166.417 KB) 
> >> > 0.000012 seconds (5 allocations: 176 bytes) 
> >> > 
> >> > 
> >> > This is a HUGE difference! Am I missing something big? Is there a 
> good 
> >> > way 
> >> > to inspect code to figure out where I am going wrong? 
> >> 
> >> This is because of type instability as you will find in the performance 
> >> tips. 
> >> Note that slow and fast are not equivalent since the fast version only 
> >> accept `A` or `B` but the slow version accepts any subtype of feature 
> >> that you may ever define. Therefore there's no way the compiler can 
> >> rewrite the slow version to the fast version. 
> >> There are optimizations that can be applied to bring down the gap but 
> >> there'll always be a large difference between the two. 
> >> 
> >> > 
> >> > 
> >> > Thank you in advance for any guidance. 
> >> > 
> >> > 
> >> > -Tim 
> >> > 
> >> > 
> >> > 
> >> > 
> >> > 
>

Reply via email to