Tuple vs vector (article)

Greetings!
I am reading Julius Krumbriegel’s (is he here?) informative article about the difference between tuples and vectors: jkrumbiegel.com - Tuples and Vectors, Allocations and Performance for Beginners

Example from the article:

rand_vector_point() = [rand(), rand()] # note the []
rand_tuple_point()  = (rand(), rand()) # note the ()

# create vectors of 500 random points each
vector_points = [rand_vector_point() for _ in 1:500]
tuple_points  = [rand_tuple_point()  for _ in 1:500]

# define a simple function calculating pairwise differences
function difference_matrix(points)
    [p1 .- p2 for p1 in points, p2 in points]
end

# run each version once, just to get compilation out of the way
difference_matrix(vector_points)
difference_matrix(tuple_points)

println("Vector version:")
@time difference_matrix(vector_points)
println("Tuple version:")
@time difference_matrix(tuple_points)

Output:

Vector version:
  0.017412 seconds (250.00 k allocations: 24.796 MiB)
Tuple version:
  0.002698 seconds (2 allocations: 3.815 MiB)

Having read the article, I am still unsure about some details.

  1. The number of allocations that are shown: Are those the ones that happen during runtime? Because part of the memory should have been allocated in one go by the compiler upon compiling the function, which would then be the reason for the small number of allocations in the tuple case I assume.

  2. The two allocations in the tuple version: Where do they come from and are they also there in the vector version? (they might have been lost in the rounding of 250.00 k).

  3. What is the relationship of the actual allocated memory and the number of allocations? The allocated memory per allocation is much larger in the tuple case than in the vector case.

3 Likes

The allocations for the tuple version are the result matrix you are constructing. That allocation happens in the Vector version also (although it will technically be an allocation of half the size since the matrix will internally store pointers to the vectors rather than the contents of the tuples.

2 Likes

Whenever the size of needed memory is not known at compile time you’ll see runtime allocations. In this case the length of points is runtime information. For most small amounts of memory known at compile time, stack memory can be used which does not need to be allocated. We don’t really have larger stack-allocated arrays in Julia, there might be some packages but the regular thing you see is StaticArrays, and that one usually doesn’t exceed on the order of a hundred or so elements because more elements are too taxing for the compiler.

Also, I’m the author of that article, nice that people still read that once in a while :smile:

9 Likes

@Oscar_Smith
But why do I need 2 allocations for one result array and not just 1 allocation?

@jules
A pleasure, resources like this are really helpful for beginners!
It seems that the results arrays, even in the tuple case, are allocated on the heap.
But the points vector of type tuple will have to be put on the stack upon compilation of the function, no?

Arrays are always stored on the heap, no matter whether the content is pointers to non-isbits data or contiguously stored isbits data.

I would say it’s put on the stack during execution of the function, but maybe you mean that the space needed there is computed during compilation of the function, which would be correct.

1 Like