I am having a hard time understanding how memory is managed by Julia.
Why do the following lines allocate memory at all?
- function winsMilestone(topSide, bottomSide, justPlayedCard::Player)
827581056 if (countnz(topSide) < 3 || countnz(bottomSide) < 3)
0 return none
- end
end
Knowing that topSide and bottomSide are of the same type and are views on a UInt8 Array?
function winsMilestone(topSide, bottomSide)
a = 0
if (countnz(topSide) < 3 || countnz(bottomSide) < 3)
return 1
end
return 2
end
function test(n_iter)
A = zeros(UInt8,(7, 9))
for i=1:n_iter
for j=1:9
@views winsMilestone(A[1:3, j], A[4:7, j])
end
end
end
test(1)
Profile.clear_malloc_data()
test(1000)
And with memory annotations:
- function winsMilestone(topSide, bottomSide)
- a = 0
-
1152000 if (countnz(topSide) < 3 || countnz(bottomSide) < 3)
0 return 1
- end
-
0 return 2
- end
-
- function test(n_iter)
304 A = zeros(UInt8,(7, 9))
0 for i=1:n_iter
0 for j=1:9
0 @views winsMilestone(A[1:3, j], A[4:7, j])
- end
- end
- end
-
- test(1)
-
- Profile.clear_malloc_data()
- test(1000)
The line a=0 is to show that memory is not associated with the first line of the function.
The problem is probably not related to countnz since if the tested expression in the if by the corresponding disjunction on all the elements of the array, the problem is the same.
I think it’s just the allocation tracker misattributing the allocation. If you create the views before the call, then winsMilestone shows no allocation:
function winsMilestone(topSide, bottomSide)
a = 0
if (countnz(topSide) < 3 || countnz(bottomSide) < 3)
return 1
end
return 2
end
function test(n_iter)
A = zeros(UInt8,(7, 9))
for i=1:n_iter
for j=1:9
A1 = @view A[1:3, j]
A2 = @view A[4:7, j]
winsMilestone(A1, A2)
end
end
end
test(1)
Profile.clear_malloc_data()
test(1000)
you end up with the *.mem output:
- function winsMilestone(topSide, bottomSide)
- a = 0
-
0 if (countnz(topSide) < 3 || countnz(bottomSide) < 3)
0 return 1
- end
-
0 return 2
- end
-
- function test(n_iter)
304 A = zeros(UInt8,(7, 9))
0 for i=1:n_iter
0 for j=1:9
576000 A1 = @view A[1:3, j]
576000 A2 = @view A[4:7, j]
0 winsMilestone(A1, A2)
- end
- end
- end
-
- test(1)
-
- Profile.clear_malloc_data()
- test(1000)
-
-
Why would a view allocate so much memory?
Also, why is A allocated using 304 bytes when the actual data it holds is 63 bytes (plus size and type information)?
In your example, 576,000 bytes / 9,000 iterations = 64 bytes whereas sizeof returns 48 bytes, but the difference is probably a matter of the underlying memory manager aligning the objects to word boundaries.
Also, why is A allocated using 304 bytes when the actual data it holds is 63 bytes (plus size and type information)?
Probably type information plus memory alignment, but you’ll probably need to dig into the C internals (or have someone who already knows the anwer comment) to verify if that accounts for all of the difference.
On today’s master, with the deprecation fixed and an @inline added it’s fixed:
julia> function test(n_iter)
A = zeros(UInt8,(7, 9))
for i=1:n_iter
for j=1:9
A1 = @view A[1:3, j]
A2 = @view A[4:7, j]
winsMilestone(A1, A2)
end
end
end
test (generic function with 1 method)
julia> @inline function winsMilestone(topSide, bottomSide)
a = 0
if (count(!iszero, topSide) < 3 || count(!iszero, bottomSide) < 3)
return 1
end
return 2
end
winsMilestone (generic function with 1 method)
julia> @time test(1000) # warum-up
0.085711 seconds (132.28 k allocations: 7.346 MiB, 7.89% gc time)
julia> @time test(1000)
0.000033 seconds (5 allocations: 304 bytes)