Thank you Tim! That is some excellent wisdom. I appreciate that and shared
it with my team.
On Friday, April 15, 2016 at 10:35:02 PM UTC+8, Tim Holy wrote:
>
> Your best bet is always to benchmark. Here's how I make such decisions:
>
> # The type-based system:
> julia> immutable Container1{T}
> val::T
> end
>
> julia> inc(::Int) = 1
> inc (generic function with 1 method)
>
> julia> inc(::Float64) = 2
> inc (generic function with 2 methods)
>
> julia> inc(::UInt8) = 3
> inc (generic function with 3 methods)
>
> julia> vec = [Container1(1), Container1(1.0), Container1(0x01)]
> 3-element Array{Container1{T},1}:
> Container1{Int64}(1)
> Container1{Float64}(1.0)
> Container1{UInt8}(0x01)
>
> julia> function loop_inc1(vec, n)
> s = 0
> for k = 1:n
> for item in vec
> s += inc(item.val)
> end
> end
> s
> end
> loop_inc1 (generic function with 1 method)
>
> # The dictionary solution
> julia> immutable Container2
> code::Symbol
> end
>
> julia> vec2 = [Container2(:Int), Container2(:Float64), Container2(:UInt8)]
> 3-element Array{Container2,1}:
> Container2(:Int)
> Container2(:Float64)
> Container2(:UInt8)
>
> julia> dct = Dict(:Int=>1, :Float64=>2, :UInt8=>3)
> Dict(:Int=>1,:UInt8=>3,:Float64=>2)
>
> julia> function loop_inc2(vec, dct, n)
> s = 0
> for k = 1:n
> for item in vec
> s += dct[item.code]
> end
> end
> s
> end
> loop_inc2 (generic function with 1 method)
>
> # The switch solution
> julia> function loop_inc3(vec, n)
> s = 0
> for k = 1:n
> for item in vec
> if item.code == :Int
> s += 1
> elseif item.code == :Float64
> s += 2
> elseif item.code == :UInt8
> s += 3
> else
> error("Unrecognized code")
> end
> end
> end
> s
> end
>
> loop_inc3 (generic function with 1 method)
>
> julia> loop_inc1(vec, 1)
> 6
>
> julia> loop_inc2(vec2, dct, 1)
> 6
>
> julia> loop_inc3(vec2, 1)
> 6
>
> julia> @time loop_inc1(vec, 10^4)
> 0.002274 seconds (10.17 k allocations: 167.025 KB)
> 60000
>
> julia> @time loop_inc1(vec, 10^5)
> 0.025834 seconds (100.01 k allocations: 1.526 MB)
> 600000
>
> julia> @time loop_inc2(vec2, dct, 10^5)
> 0.010278 seconds (6 allocations: 192 bytes)
> 600000
>
> julia> @time loop_inc3(vec2, 10^5)
> 0.001561 seconds (6 allocations: 192 bytes)
> 600000
>
>
> So in terms of run time, the bottom line is:
> - The "switch" version is fastest (by quite a lot), but ugly.
> - The dictionary is intermediate. You would likely be able to do even
> better
> with a "perfect hash" dictionary, see
> http://stackoverflow.com/questions/36385653/return-const-dictionary
> - The type-based solution is slowest, but not much worse than the
> dictionary.
>
> Note that none of this analysis includes compilation time. If you're
> writing a
> large system, the type-based one in particular will require longer JIT
> times,
> whereas the first two get by with only a single type and hence will need
> much
> less compilation.
>
> Of course, if `inc` were a complicated function, it might change the
> entire
> calculus here. That's really the key: what's the tradeoff between the
> amount of
> computation per element and the price you pay for dispatch to a type-
> specialized method? There is no universal answer to this question.
>
> Best,
> --Tim
>
>