Alllocations in a reduction over a Tuple

A warm welcome to the community.

I think the problem you do encounter is that

ntuple(Val(length(x))) do i 
    Base.@_inline_meta
    if !(x[i] isa Tuple)
        c[]+= f(x[i], rand())
    else
        c[]+= 1/(foo(x[i]))
    end
end

creates a tuple, which is not used, but as it seems not optimized away by the compiler.


You can get what you want by using a different kind of meta-programming magic.

bar(x) = bar(x,Val(length(x)))

@inline @generated function bar(x,::Val{N}) where N
	quote
		c = 0.0
		Base.Cartesian.@nexprs $N i -> c += x[i] isa Tuple ? 1/(bar(x[i],Val(length(x[i])))) : f(x[i],rand())
		return c
	end
end

yields

a = myStruct1(1)
b = myStruct2(1)
x1 =  ((b, (a, b)), (((a, (a,(b,b))), a), b))

@btime bar($x1) # 36.172 ns (0 allocations: 0 bytes)
@btime foo($x1) # 154.228 ns (12 allocations: 432 bytes)

On your side note. If you evaluate foo(x1) once, prior to the benchmark, both benchmarks yield the same result. Actually, this should not happen with BenchmarkTools. I have no idea why it does.

1 Like