Matrix to array of tuples: Avoiding many small allocations

How do I convert a matrix to an array of tuples while avoiding many small allocations in a loop? My current code is

function matrixtotuple(x::AbstractMatrix{T}) where T
	r,c = size(x)	
	pts = Vector{Tuple}(undef,r)
	@time @inbounds for i in 1:r
		pts[i] = Tuple(x[i,:])
	end
	return pts
end

For a 20x2 matrix, the loop does 100 allocations which means its 5 allocations per row. For a large matrix with millions of entries, it will perform many small allocations thus slowing down my code. Is there a better way to do this operation without allocating in the loop?

It’s probably easier to just do

tuple.(eachcol(m)...)

With StructArrays you could create a custom array type such that indexing into it gives you tuples, while reusing memory, which can be useful if the original array is big:

julia> using StructArrays

julia> m = rand(20, 2);

julia> sa = StructArray(Tuple(eachcol(m)))
20-element StructArray(view(::Array{Float64,2}, :, 1), view(::Array{Float64,2}, :, 2)) with eltype Tuple{Float64,Float64}:
 (0.4970326273287038, 0.21143138008431217)
 (0.29962208131086276, 0.061704834464211045)
 (0.4966772487171329, 0.9175385362922233)
 (0.527788713877285, 0.7280621891438683)
 (0.6150731201744135, 0.00070954809832946)
 (0.9146189735972257, 0.5957334145094078)
 (0.6407960691283592, 0.44134204020165546)
 (0.6772847683943304, 0.0652426344276058)
 (0.6124967642056414, 0.09899733105729025)
 (0.52764483080016, 0.22016424658251132)
 (0.07246528707313882, 0.7648818239756578)
 (0.6289503443358524, 0.8154583933386323)
 (0.718739296080896, 0.776853087938548)
 (0.22946316921846432, 0.1630589657899728)
 (0.9242116187812148, 0.7729709003381926)
 (0.35424126211091633, 0.7732465640412218)
 (0.786801141532484, 0.9334757632322519)
 (0.18693070616317198, 0.20233848154960543)
 (0.2922497465234608, 0.06163461119766822)
 (0.8558830208857984, 0.21699499637318542)
3 Likes

The StructArray is just what I need. For a 20x2 matrix, this is the benchmark:

x = rand(20,2)
@btime y = matrixtotuple($x);
  7.025 μs (101 allocations: 5.23 KiB)

@btime tuple.(eachcol($x)...);
  772.165 ns (9 allocations: 656 bytes)

@btime sa = StructArray(Tuple(eachcol($x)));
  384.724 ns (7 allocations: 288 bytes)

Thanks for your help.

1 Like