On Saturday, April 2, 2016 at 5:39:45 PM UTC-4, Greg Plowman wrote:
>
> Cedric,
> On my machine fast() and pretty_fast() run in the roughly the same time.
> Are you sure pre-compiled first?
>
Yep. I'm using @benchmark on Julia 0.4.5 on OSX, and the time difference is
definitely > 2X.
>
> 1. If you add a default fallback method, say, evaluate(f::Feature) = -1.0
> Theoretically, would that inform the compiler that evaluate() always
> returns a Float64 and therefore is type stable?
>
No, because I might write
type C <: Feature
end
evaluate(f::C) = 100
and a Vector{Feature} might contain a C. Abstract types are open-ended, new
types can be created a runtime, so the compiler can't assume anything about
them, which is why they're not useful for performance. See here
<http://docs.julialang.org/en/release-0.4/manual/performance-tips/#avoid-fields-with-abstract-type>
.
> Does dynamic dispatch mean compiler has to look up method at run time
> (because it can't know type ahead of time).
> This is equivalent to explicitly coding "static dispatch" with if
> statements?
> If so then why is it so much slower?
> Providing a fallback method as in 1. or explicitly returning Float64
> doesn't seem to improve speed, which naively suggests that slowness is from
> dynamic dispatch not from type instability???
>
> 3. Also, on my machine pretty_fast() is as fast as fast(). Why is this so,
> if pretty_fast() is supposedly type unstable?
>
> As you can probably tell, I'm pretty confused on this.
>
>
> On Sunday, April 3, 2016 at 6:34:09 AM UTC+10, Cedric St-Jean wrote:
>
>> Thank you for the detailed explanation. I tried it out:
>>
>> function pretty_fast(features::Vector{Feature})
>> retval = 0.0
>> for i in 1 : length(features)
>> if isa(features[i], A)
>> x = evaluate(features[i]::A)
>> elseif isa(features[i], B)
>> x = evaluate(features[i]::B)
>> else
>> x = evaluate(features[i])
>> end
>> retval += x
>> end
>> retval
>> end
>>
>> On my laptop, fast runs in 10 microseconds, pretty_fast in 30, and slow
>> in 210.
>>
>> On Saturday, April 2, 2016 at 12:24:18 PM UTC-4, Yichao Yu wrote:
>>>
>>> On Sat, Apr 2, 2016 at 12:16 PM, Tim Wheeler <[email protected]>
>>> wrote:
>>> > Thank you for the comments. In my original code it means the
>>> difference
>>> > between a 30 min execution with memory allocation in the Gigabytes and
>>> a few
>>> > seconds of execution with only 800 bytes using the second version.
>>> > I thought under-the-hood Julia basically runs those if statements
>>> anyway for
>>> > its dispatch, and don't know why it needs to allocate any memory.
>>> > Having the if-statement workaround will be fine though.
>>>
>>> Well, if you have a lot of these cheap functions being dynamically
>>> dispatched I think it is not a good way to use the type. Depending on
>>> your problem, you may be better off using a enum/flags/dict to
>>> represent the type/get the values.
>>>
>>> The reason for the allocation is that the return type is unknown. It
>>> should be obvious to see if you check your code with code_warntype.
>>>
>>> >
>>> > On Saturday, April 2, 2016 at 7:26:11 AM UTC-7, Cedric St-Jean 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
>>> >>
>>> >> 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
>>> >>> >
>>> >>> >
>>> >>> >
>>> >>> >
>>> >>> >
>>>
>>