Error: Undefined variable, inside a macro in a for loop in a function of a module


I defined the following function and included it in a module:

function benchmark(Mdata, F, JF, Exact, y0, β, par, H)

    Bench = zeros(length(H), 2, 2)
    t = zeros()
    y = zeros()
    t1 = zeros()
    y1 = zeros()
    h = 0

    for i in 1:length(H)

        h = H[i]

        Bench[i, 1, 1] = mean(@benchmark FDEsolver(F, tSpan, y0, β, par, h = h)).time * 10e-10
        Bench[i, 2, 1] = mean(@benchmark FDEsolver(F, tSpan, y0, β, par, J = JF, h = h)).time * 10e-10

        if i >= 4

            t, y = FDEsolver(F, tSpan, y0, β, par, h = h)
            t1, y1 = FDEsolver(F, tSpan, y0, β, par, J = JF, h = h)
            Bench[i, 1, 2] = norm(y - map(Exact, t), 2)
            Bench[i, 2, 2] = norm(y1 - map(Exact, t), 2)



    # plot logplot of execution time
    plot(H, Bench[:, :, 1], linewidth = 5, xscale = :log, yscale = :log, title = "Benchmark for Example 1", yaxis = "Time (Sc)", xaxis = "Step size", label = ["PI_PC" "PI_IM"])
    plot!(H, Mdata[!, 1], linewidth = 5, xscale = :log, yscale = :log, label = "M_PI_PC")
    plot!(H, Mdata[!, 2], linewidth = 5, xscale = :log, yscale = :log, label = "M_PI_IM")

    # plot square norm of the error
    plot(H[4:end], Bench[4:end, :, 2], yscale = :log, xscale = :log, linewidth = 5, title = "Square norm of the errors", yaxis = "Errors", xaxis = "Step size", label = ["PI_PC" "PI_IM"])
    plot!(H[4:end], Mdata[4:end, 3], linewidth = 5, xscale = :log, ls = :dash, yscale = :log, label = "M_PI_PC")
    plot!(H[4:end], Mdata[4:end, 4], linewidth = 5, xscale = :log, yscale = :log, ls = :dot, label = "M_PI_IM")


The relevant part is the macro @benchmark inside the for loop. When I try to use this function, I get the error that the h inside the macro is not defined, even though I clearly defined it and when I debug h outside of the macro it says it is defined.

What am I doing wrong?


If I do

benchmark(MatlabBenchmark.Mdata1, F, JF, Exact, y0, β, par, H)

I get the error:

UndefVarError: h not defined
var"##core#257"() at execution.jl:479
var"##sample#258"(__params::BenchmarkTools.Parameters) at execution.jl:485
_run(b::BenchmarkTools.Benchmark, p::BenchmarkTools.Parameters; verbose::Bool, pad::String, kwargs::Base.Iterators.Pairs{Symbol, Integer, NTuple{4, Symbol}, NamedTuple{(:samples, :evals, :gctrial, :gcsample), Tuple{Int64, Int64, Bool, Bool}}}) at execution.jl:98
(::BenchmarkTools.var"#_run##kw")(::NamedTuple{(:verbose, :samples, :evals, :gctrial, :gcsample), Tuple{Bool, Int64, Int64, Bool, Bool}}, ::typeof(BenchmarkTools._run), b::BenchmarkTools.Benchmark, p::BenchmarkTools.Parameters) at execution.jl:92
#invokelatest#2 at essentials.jl:710 [inlined]
invokelatest at essentials.jl:706 [inlined]
#run_result#45 at execution.jl:33 [inlined]
run_result at execution.jl:33 [inlined]
run(b::BenchmarkTools.Benchmark, p::BenchmarkTools.Parameters; progressid::Nothing, nleaves::Float64, ndone::Float64, kwargs::Base.Iterators.Pairs{Symbol, Integer, NTuple{5, Symbol}, NamedTuple{(:verbose, :samples, :evals, :gctrial, :gcsample), Tuple{Bool, Int64, Int64, Bool, Bool}}}) at execution.jl:116
run at execution.jl:116 [inlined]
run at execution.jl:116 [inlined]
#warmup#54 at execution.jl:168 [inlined]
warmup(item::BenchmarkTools.Benchmark) at execution.jl:168
macro expansion at execution.jl:387 [inlined]
benchmark(Mdata::DataFrames.DataFrame, F::Function, JF::Function, Exact::Function, y0::Int64, β::Float64, par::Int64, H::Vector{Float64}) at benchmark.jl:14
top-level scope at matlab_benchmark1.jl:22
eval at boot.jl:360 [inlined]

@benchmark operates at global scope, so you need to use $ to interpolate any local values. This is kind of a quirk of BenchmarkTools in particular, and you’re far from the first person to run into it. In your case, this would look like @benchmark FDEsolve($F, $tSpan, ....).

More info here: Manual · BenchmarkTools.jl