# How to write a fast loop through structure fields

Hi all,
Novice question. I am looping through structure fields and would like to know how to keep this fast. Is there a way to work on these that is faster?

``````#Function that works on values
function test(x,y,z)
x=x.+1
y=y.+1
z=z.+1
return x,y,z
end

#Function that loops thorugh structure entries and does the same
function test2(Structure)
FieldsInStruct=fieldnames(typeof(Structure));
for i=1:length(FieldsInStruct)
#Check field i
Value=getfield(Structure, FieldsInStruct[i])
Value=Value.+1;
setfield!(Structure,FieldsInStruct[i],Value)
end
end

x=zeros(10);
y=ones(10);
z=ones(10).+1;

mutable struct points; x;y;z; end
PntLst=points(x,y,z)

#Run two times...
@time test(x,y,z)
#0.000005 seconds (7 allocations: 640 bytes)
@time test2(PntLst)
#0.000036 seconds (14 allocations: 816 bytes)
``````

Is there a way for me to get this second function faster, still without explictly typing out the structures field names?

`mutable struct points; x;y;z; end`

Since you care about speed, you may want to add types here, e.g.

`mutable struct points{T}; x::T; y::T; z::T; end`

As for your question, you’ll get something much more readable and speedy using metaprogramming. For example:

``````@generated function test4(p::P) where P
assignments = [
:( p.\$name += 1 ) for name in fieldnames(P)
]
quote \$(assignments...) end
end
``````

If you find yourself wanting to loop through `struct` fields, it’s quite likely you are using the wrong data structure.

For example, if you want an array of (x,y,z) coordinates, then rather than a `struct` with (`x`,`y`,`z`) fields I would just use an `Array` of `SVector` from StaticArrays.jl:

``````using StaticArrays
pointlist = fill(SVector(0.0, 1.0, 2.0), 10)
``````

By using the StaticArrays package, you get extremely fast and convenient geometric operations on the points. e.g. to shift all of the coordinates by (1,1,1) you would just do:

``````pointlist .+= Ref(SVector(1,1,1))
``````

or even just

``````pointlist .+= 1
``````
5 Likes

Great, will try both. Thanks.

Occasionally yes. But it would still be interesting to have a programming technique where one could write code that is equivalent to manually unrolled versions without using a generated function. Eg, for an MWE, consider

``````struct HetPoint{T,S}
x::T
y::S
end

plus_manual(a::HetPoint, b::HetPoint) = HetPoint(a.x + b.x, a.y + b.y)

plus_loop(a::HetPoint, b::HetPoint) =
HetPoint(ntuple(i -> getfield(a, i) + getfield(b, i), Val(fieldcount(typeof(a))))...)
``````

where

``````julia> using BenchmarkTools

julia> a = HetPoint(1.0, 2)
HetPoint{Float64,Int64}(1.0, 2)

julia> b = HetPoint(1f0, Int8(2))
HetPoint{Float32,Int8}(1.0f0, 2)

julia> @btime plus_manual(\$a, \$b)
0.032 ns (0 allocations: 0 bytes)
HetPoint{Float64,Int64}(2.0, 4)

julia> @btime plus_loop(\$a, \$b)
2.853 ns (0 allocations: 0 bytes)
HetPoint{Float64,Int64}(2.0, 4)
``````

I can occasionally optimize these things, but have not found a general way so far.

1 Like