The basic thing to keep in mind is that Julia’s compiler (like many compilers) specializes code mainly based on the types of objects, not based on their values (which can change at runtime). [i,j,k]
is a Vector{Int}
, which does not contain the length of the array in the type. In principle, the compiler could be made clever enough to infer it in this case — essentially by constant propagation — since [i,j,k]
appears literally in the source code, though it doesn’t currently do this AFAIK. (Even that analysis can be tricky, e.g. because various functions can resize a Vector
at runtime.) But what about all of your other vectors, like neighboratom
? How is the compiler supposed to know their lengths?
In contrast, @SVector[i,j,k]
creates an object of type SVector{3,Int}
, where the length 3
is part of the type (and internally to the SVector
the data is stored in a Tuple{Int,Int,Int}
, for which the length is also part of the type, and the compiler has special optimizations for tuples). So, the compiler can specialize on this in several ways:
- local-variable
SVector
objects need not be stored on the heap, and can be stored on the stack or in registers (exactly as if the individual components were separate scalar variables) - arrays of
SVector
objects can store them “inline” in the array data. e.g. aVector{SVector{3,Float64}}
stores an arbitrary number of 3-componentSVector
s, each as 3 numbers one after another in the array data (exactly like an array ofFloat64
of 3x the length). And when you access an element of the array, the compiler knows it is a 3-componentSVector
and can specialize the resulting code using (1) and (3). - operations on
SVector
can be specialized on the length and completely unrolled. For example, adding twoSVector
s can be accomplished with 3 scalar additions and no loop.