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 
>
>

Reply via email to