Integrating functions with additional parameters

Hello everyone,

I have a question regarding performance of the following mwe:

using BenchmarkTools

function integrate!(f!, buf, a, b, N)
    
    h    = (b - a) / N
    buf .= 0.0
    
    for i in 1 : N
        f!(buf, a + i * h, h)
    end 
    
    return nothing
end

function f!(b, x, dx)
    
    for i in eachindex(b)
        b[i] += x * dx 
    end 
    
    return nothing 
end

function g!(b, x, dx, a)
    
    for i in eachindex(b)
        b[i] += a[i] * x * dx 
    end 
    
    return nothing 
end

let  
    buf = zeros(10)
    a   = rand(10)

    h!(b, x, dx) = g!(b, x, dx, a)

    @btime integrate!((b, x, dx) -> f!(b, x, dx), buf, 0.0, 1.0, 100) # 474.847 ns (0 allocations: 0 bytes)
    @btime integrate!((b, x, dx) -> h!(b, x, dx), buf, 0.0, 1.0, 100) # 2.857 μs (200 allocations: 3.13 KiB)
end

Is there a way to get rid of the allocations for the integration of h!? In my application I have to compute several of these integrals with different input arrays (and structs) and the performance loss seems quite substantial …

Your let block isn’t actually working here because @btime operates in global scope. Running your code in a fresh Julia session gives:

julia> let  
           buf = zeros(10)
           a   = rand(10)

           h!(b, x, dx) = g!(b, x, dx, a)

           @btime integrate!((b, x, dx) -> f!(b, x, dx), buf, 0.0, 1.0, 100) # 474.847 ns (0 allocations: 0 bytes)
           @btime integrate!((b, x, dx) -> h!(b, x, dx), buf, 0.0, 1.0, 100) # 2.857 μs (200 allocations: 3.13 KiB)
       end
ERROR: UndefVarError: buf not defined

because btime is trying to find the global variable buf (and h!). Presumably those globals exist in your current Julia session, so you’re benchmarking access to those globals, not whatever’s in your let block.

The solution is the same as always: use $ to interpolate when benchmarking: GitHub - JuliaCI/BenchmarkTools.jl: A benchmarking framework for the Julia language

julia> let  
           buf = zeros(10)
           a   = rand(10)

           h!(b, x, dx) = g!(b, x, dx, a)

           @btime integrate!((b, x, dx) -> f!(b, x, dx), $buf, 0.0, 1.0, 100) 
           @btime integrate!((b, x, dx) -> $h!(b, x, dx), $buf, 0.0, 1.0, 100) 
       end
  441.480 ns (0 allocations: 0 bytes)
  800.731 ns (0 allocations: 0 bytes)
3 Likes

Whoops, you’re right. Did not read the BenchmarkTools manual close enough. Thanks!