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