Memory allocation during assignment and modification of Float64 variables

Hello, I have come across really puzzling (at least for me) output of julia --track-allocation=user. After playing with my code and outputs of benchmarking tools, I have ended up with the following snippet of *.mem file, which shows the main bottleneck in both the memory allocation and speed.

      160 		values = hcubature([0.,0.,0.,0.,0.],[2*pi,pi,rlimit,2*pi,pi],norm=norm,rtol=rtol,atol=atol,maxevals=10000) do point
    45192 			l = zero(Int64)
        0 			integral1,err1 = quadgk(0.,point[3] - singularitybound,rtol=rtolinner,atol=atolinner) do x
    34164 				l+=1
        0 				value = one(Float64)
        0 				value = value
        0 				helper::Float64 = real(f(a,b,c,d,Z,point[1],point[2],point[3],point[4],point[5],x))
  2460372 				value *= helper
        - 				# println(typeof(helper))
        - 				# println(typeof(value))
        0 				helper2::Float64 = real(point[3]^2*sin(point[2])*x^2*sin(point[5]))
  2460372 				value *= helper2
        - 				# println(typeof(value))
        0 				value
        - 			end
        0 			integral2,err2 = quadgk(point[3]+singularitybound,rlimit,rtol=rtolinner,atol=atolinner) do x
   480708 				 l+=1
  4363680 				value = f(a,b,c,d,Z,point[1],point[2],point[3],point[4],point[5],x)*point[3]^2*sin(point[2])*x^2*sin(point[5])
        0 				value
        - 			end
   366500 			println("l: $(l)")
   120480 			return integral1 + integral2
        - 		end

This code is part of a function and there are no global variables used. f is of type Function and variables a, b, c and d are of type NTuple{3,Int64}. The function f does not allocate anything.

Do you understand why is seems that Julia allocates memory when modifying variables of types Int64 and Float64? This output just doesn’t seem right to me, but I am relatively new to Julia. I am almost sure that there is no type instability.

This might be some artifact from compilation. Did you run the function once and then reset the counter as described in the docs below?
https://docs.julialang.org/en/v1/manual/profile/#Line-by-Line-Allocation-Tracking

Other than that there is the infamous case where captured variables lead to type instabilities but this doesn’t seem to be the case here unless you have a variable named value outside of the snippet you showed.

2 Likes

Ok, I was not doing that. After resetting the counter, assignment to the value variable is not allocating. However, modification of variable l still allocates even when using let statement

      928 		values = hcubature([0.,0.,0.,0.,0.],[2*pi,pi,rlimit,2*pi,pi],norm=norm,rtol=rtol,atol=atol,maxevals=10000) do point
        - 			let l = zero(Int32)
        - 				quadgk(0.,point[3] - singularitybound,rtol=rtolinner,atol=atolinner) do x
  6403224 					l+=1
        - 				    value = one(Float64)
        - 					value = value
        0 					helper::Float64 = real(f(a,b,c,d,Z,point[1],point[2],point[3],point[4],point[5],x))
        0 					value =value* helper
        0 					helper2::Float64 = real(point[3]^2*sin(point[2])*x^2*sin(point[5]))
        0 					value =value* helper2
        0 					value
        - 				end
        - 			end
        - 			return 0.
        - 		end

Maybe, I don’t understand how Julia works with closures, but I would expect that the line let l = zero(Int32) allocates memory on heap and closure references this part of memory and modifies it.

There is no other variable value nor l in my project outside this snippet.

I do not understand what the l is good for. I assume it’s some kind of tally. However, it’s always risky to update captured variables in julia. You should rather use a Ref:

let l = Ref(zero(Int32))
...
    l[] += 1
...
end

You will get an allocation with the Ref. You can move it outside the call to hcubature and just zero it inside with l[] = 0.