Mpala
Mpala

Reputation: 51

Julia: Struct with constant field to optimize performance

foo is a callable example struct that will have additional fields later. Since some of these fields hold arrays with changing values it's not an option to make the instance a const G. However, the boolean value b stays constant. Is there a way to tell the compiler that this field will never change in order to enable optimizations? In this example I would like F to be as fast as G and H.

using BenchmarkTools

struct foo
  b::Bool
end

function (f::foo)(x)
  if f.b
    x+1
  end
end

F = foo(true)

const G = foo(true)

H(x) = x+1


@btime F(1) # 12.078 ns (0 allocations: 0 bytes)

@btime G(1) # 0.023 ns (0 allocations: 0 bytes)

@btime H(1) # 0.024 ns (0 allocations: 0 bytes)

Upvotes: 2

Views: 403

Answers (1)

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69949

You are not benchmarking the F correctly. Here is a way to do it:

julia> @btime $F(1)
  0.026 ns (0 allocations: 0 bytes)
2

The problem is not that F is a global non-constant variable while G is a constant, so when accessing F its value is not stable within @btime.

Adding a $ in front of the call makes F a local variable during a benchmark and you can then see that it is equally fast.

Also it is probably better to benchmark some larger functions in such cases. Here is a quick example:

julia> function test(x)
       s = 0
       for i in 1:10^6
           s += x(1)
       end
       s
       end
test (generic function with 1 method)

julia> @btime test($F)
  35.830 μs (0 allocations: 0 bytes)
2000000

julia> @btime test($G)
  35.839 μs (0 allocations: 0 bytes)
2000000

Also you can check with @code_native that F and G end up with identical native code:

julia> @code_native F(1)
    .text
; ┌ @ REPL[10]:2 within `foo'
    cmpb    $0, (%rsi)
    je  L17
; │ @ REPL[10]:3 within `foo'
; │┌ @ int.jl:53 within `+'
    addq    $1, %rdx
; │└
    movq    %rdx, (%rdi)
    movb    $2, %dl
    xorl    %eax, %eax
    retq
L17:
    movb    $1, %dl
    xorl    %eax, %eax
; │ @ REPL[10]:3 within `foo'
    retq
    nopw    %cs:(%rax,%rax)
; └

julia> @code_native G(1)
    .text
; ┌ @ REPL[10]:2 within `foo'
    cmpb    $0, (%rsi)
    je  L17
; │ @ REPL[10]:3 within `foo'
; │┌ @ int.jl:53 within `+'
    addq    $1, %rdx
; │└
    movq    %rdx, (%rdi)
    movb    $2, %dl
    xorl    %eax, %eax
    retq
L17:
    movb    $1, %dl
    xorl    %eax, %eax
; │ @ REPL[10]:3 within `foo'
    retq
    nopw    %cs:(%rax,%rax)
; └

Upvotes: 2

Related Questions