# Dispatch and Symbols

**URL:** <https://discourse.julialang.org/t/dispatch-and-symbols/21162>\
**Category:** New to Julia\
**Created:** [February 25, 2019, 4:24am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162 "2019-02-25T04:24:35Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![askvorts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/askvorts/32/7120_2.png) [@askvorts](https://discourse.julialang.org/u/askvorts)\
**Post date:** [February 25, 2019, 4:24am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/1 "2019-02-25T04:24:35Z")

</div>

Apologize if this is trivial but couldn’t find a solution reading the manual:

```julia
module easy
abstract type Math end
struct Mul <: Math end
struct Div <: Math end

function op(a, b, ::Mul)
    return a*b
end
function op(a, b, ::Div)
    return a/b
end

export op
export Mul
export Div

end # module

```

How may I change the above module so that instead of

```julia
julia> op(2,3,Mul())
6

```

would be able to do

```julia
Julia> op(2, 3, :Mul)

```

(`Mul`, `Div`,… are actually names of `DataFrame` columns that were previously read)  
Thank you!

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 25, 2019, 4:30am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/2 "2019-02-25T04:30:01Z")

</div>

```julia
function op(a, b, ::Type{Mul})
    return a*b
end

julia> op(2, 3, Mul)
6

```

While the above does not dispatch on `:Mul`, it may be of use.

```julia
function op(a, b, ::Val{:Mul})
    return a * b
end

julia> op(2, 3, Val(:Mul))
6

```

if the structs are not defined

```julia
const dataframe_col_name = :Mul
const Mul = Val(dataframe_col_name)

function op(a, b, ::Val{dataframe_col_name})
    return a * b
end

julia> op(2, 3, Mul)
6

```

---

<div class="post-metadata">

**Author:** ![askvorts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/askvorts/32/7120_2.png) [@askvorts](https://discourse.julialang.org/u/askvorts)\
**Post date:** [February 25, 2019, 4:36am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/3 "2019-02-25T04:36:42Z")

</div>

Beautiful! Thank you very much!

---

<div class="post-metadata">

**Author:** ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)\
**Post date:** [February 25, 2019, 6:28am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/4 "2019-02-25T06:28:30Z")

</div>

A simpler and less repetitious approach is to use a dictionary. Think about how many places you need to change if you add another operation. I see three in your code. I like to strive for just one. A dictionary is not as efficient as dispatch, but perhaps you don’t need that. (You could of course use `if` statements instead of a dictionary for better performance.)

```julia
const op = Dict(
    :Mul => (a,b) -> a * b,
    :Div => (a,b) -> a / b)

```

Then:

```julia
julia> op[:Mul](2, 3)
6

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 25, 2019, 9:11am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/5 "2019-02-25T09:11:41Z")

</div>

> [@bennedich](#):
>
> A dictionary is not as efficient as dispatch,

A dictionary is much faster than dynamic dispatch which is what is lwhat will happen here.

But switching on an e.g. an enum with all the different opcodes will most likely be the fastest.

---

<div class="post-metadata">

**Author:** ![Olivier\_Merchiers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/olivier_merchiers/32/4073_2.png) [@Olivier\_Merchiers](https://discourse.julialang.org/u/Olivier_Merchiers)\
**Post date:** [February 25, 2019, 9:43am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/6 "2019-02-25T09:43:13Z")

</div>

Could you give a small example of this approach?

> But switching on an e.g. an enum with all the different opcodes will most likely be the fastest.

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 25, 2019, 10:09am UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/7 "2019-02-25T10:09:12Z")

</div>

Something like

```julia
@enum OpCode begin
   MUL
   DIV
end

function evaluate_op(op::OpCode, a, b)
    if op == MUL
        return a * b
    elseif op == DIV
       return a / b
    end
    error("should not happen")
end

```

```julia
julia> evaluate_op(MUL, 2, 3)
6

```

It’s pretty easy to metaprogram the big switch if one wants a bit neater code.

---

<div class="post-metadata">

**Author:** ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)\
**Post date:** [February 25, 2019, 1:42pm UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/8 "2019-02-25T13:42:51Z")

</div>

Yep that’s the `if` solution I referred to. But the point I was trying to make was more to think about if you really need to eke every last nanosecond out of this evaluation? If not, I’d primarily focus on simple, [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), readable code.

Btw, in the suggestion above, the `error` prevents SIMDing the evaluation. If you can guarantee the validity of the input, and the surrounding code also allows for SIMD, you can speed up the evaluation greatly by removing that check. In fact, even if you can’t guarantee the input, you can still do much better by first looping over the data to ensure that it’s valid (which will be fast, since there will be virtually zero branch mispredictions), and then loop over and evaluate the operation. Sample code:

```julia
@enum OpCode begin
    MUL
    DIV
end

# SIMD unfriendly
function evaluate_op(op::OpCode, a, b)
    if op == MUL
        return a * b
    elseif op == DIV
        return a / b
    end
    error("should not happen")
end

# SIMD friendly
function evaluate_op2(op::OpCode, a, b)
    if op == MUL
        return a * b
    else
        return a / b
    end
end

N = 100_000
a, b, v = 1:3 .|>_-> rand(N)
ops = rand([MUL, DIV], N)

```

Timings:

```julia
julia> @btime $v .= evaluate_op.($ops, $a, $b);
  393.465 μs (0 allocations: 0 bytes)

julia> @btime $v .= evaluate_op2.($ops, $a, $b);
  57.408 μs (0 allocations: 0 bytes)

```

Optionally, verifying input:

```julia
julia> @btime for op = $ops; op < MUL || op > DIV && error("bad!"); end
  39.312 μs (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 25, 2019, 2:38pm UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/9 "2019-02-25T14:38:50Z")

</div>

> Btw, in the suggestion above, the `error` prevents SIMDing the evaluation.

- Using only 2 opcodes might make the conclusions not apply (your version seems to have completely avoided using a lookup table at all, which seems special cased for 2).
- I doubt this actually SIMDs even without `error`. Do you actually see SIMD instructions?

---

<div class="post-metadata">

**Author:** ![Olivier\_Merchiers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/olivier_merchiers/32/4073_2.png) [@Olivier\_Merchiers](https://discourse.julialang.org/u/Olivier_Merchiers)\
**Post date:** [February 25, 2019, 5:15pm UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/10 "2019-02-25T17:15:42Z")

</div>

thanks a lot!

---

<div class="post-metadata">

**Author:** ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)\
**Post date:** [February 25, 2019, 7:32pm UTC](https://discourse.julialang.org/t/dispatch-and-symbols/21162/11 "2019-02-25T19:32:42Z")

</div>

> [@kristoffer.carlsson](#):
>
> I doubt this actually SIMDs even without `error` . Do you actually see SIMD instructions?

Yes it SIMDs. First, there’s an easy and mostly accurate way to test if an expression SIMDs or not without scrutinizing the code: run it on data with half the number of bits. Properly SIMDed code should then see a close to 2x speedup. Testing with the above code, first 64 bit floats:

```julia
julia> N = 100_000; ops = rand([MUL, DIV], N);

julia> a, b, v = 1:3 .|>_-> rand(N);

julia> @btime $v .= evaluate_op.($ops, $a, $b);
  393.465 μs (0 allocations: 0 bytes)

julia> @btime $v .= evaluate_op2.($ops, $a, $b);
  57.408 μs (0 allocations: 0 bytes)

```

Now 32 bit floats:

```julia
julia> a, b, v = 1:3 .|>_-> rand(Float32, N);

julia> @btime $v .= evaluate_op.($ops, $a, $b);
  403.560 μs (0 allocations: 0 bytes)

julia> @btime $v .= evaluate_op2.($ops, $a, $b);
  27.580 μs (0 allocations: 0 bytes)

```

So `evaluate_op2` most likely SIMDs. To verify, and see how:

```julia
julia> @code_native syntax=:intel broadcast!(evaluate_op2, v, ops, a, b)
...
L1536:
	vmovups	ymm1, ymmword ptr [rdx + 4*rbx]
	vmovups	ymm2, ymmword ptr [r11 + 4*rbx]
	vpcmpeqd	ymm3, ymm0, ymmword ptr [rcx + 4*rbx]
	vdivps	ymm4, ymm1, ymm2
	vmulps	ymm1, ymm1, ymm2
	vblendvps	ymm1, ymm4, ymm1, ymm3
	vmovups	ymmword ptr [rsi + 4*rbx], ymm1
	add	rbx, 8
	cmp	rdi, rbx
	jne	L1536
...

```

It’s quite clever, what this code does: It loads 256 bits of values at a time from each of the `a` and `b` input vectors (on my AVX2 laptop, newer CPUs would presumably work with 512 bits). That is 4x 64 bit floats, or 8x 32 bit floats. It then _both multiplies and divides all values_ with each other, and finally selects (blends) either the product or quotient depending on the operation for each value.

Although only four 64 bit floats are processed at a time, and although the code does additional work (always both multiplies and divides), one might wonder why it’s a whopping 7 times faster than the non-SIMD version. The answer is that the code above is _completely branchless_ (apart from the loop condition), whereas the non-SIMD version will incur constant costly branch misses. To see that this is the case, we can use a smaller vector, so that the CPU will learn the branching behavior during the benchmark:

```julia
julia> N = 1000; ops = rand([MUL, DIV], N);

julia> a, b, v = 1:3 .|>_-> rand(N);

julia> @btime $v .= evaluate_op.($ops, $a, $b);
  1.211 μs (0 allocations: 0 bytes)

julia> @btime $v .= evaluate_op2.($ops, $a, $b);
  572.929 ns (0 allocations: 0 bytes)

```

Now, without branch mispredictions, the SIMD version is only ~2x faster than the SIMD version, which intuitively makes sense (processes 4 values per iteration, but does 2x the amount of work).

And no, this particular pattern won’t scale very well if more operations are added.

Sorry, long post, probably irrelevant to the OP, I got a bit carried away there.
