I have a rather large and complicated mutable struct which I use to just house pre-allocated Vector{SArray} 's of a system. I can then pass this struct to my functions which get called many times without allocating which is great!
The size of those matrices and vectors are dependent on the inputs to the system. I have been using type parameters to specify size where possible i.e.
q::Vector{SArray{S,T,1} where S<:Tuple}
but the system is rather complicated (~100 fields), so the struct definition is rather messy, and I’ve spent a few hours now tracking down method errors. This got me thinking, do I even need to do this? If I just don’t define the type of the field, my struct constructor will output exactly the right type, and then just operate on that fully defined struct. Once the constructor creates the mutable struct container, the size and type of the fields never changes for that system or its functions.
I think I may still want to define the field types just for user input verification, but is there any performance reason to define it? Could type instability still be an issue, even if the mutable struct is fully defined at construction automatically?
Typically a function barrier around the performance sensitive loops solves that kind of problem (assuming each loop uses only a reasonable subset of the 100+ struct fields). Something along the lines of
mutable struct Foo
x
y
z
end
function f(a)
# Run time dispatch
loop1(a.x, a.y, a.z)
end
function loop1(x, y, z)
for x1 in x
# etc - static dispatch
end
end
a = Foo([1,2], [2.0, 3.0], [4,5])
f(a)
I seem to remember there was a small package with a macro that automatically adds type parameters to a struct, but I can’t find it. Anyone know what I’m talking about?
I’m not quite sure about this signature. Is it supposed to represent many different dimensionalities? Putting the where clause on the field inside the struct means the field will not be concrete I think.
Thanks for the reference. Sounds like I definitely want to specify the field types using what seems like could be any of the techniques listed in the thread so far. I was doing something similar to your recommendation albeit more manually by just creating the structures, typeof() on the field, and setting it to that.
This is very interesting. You’re correct that I have many functions that have a subset of the fields as arguments. Actually, I already have a wrapper Foo that I feed the whole struct to, and then a dispatched method Foo that gets fed all of the arguments it needs that I was using to initialize the structs. I’m definitely going to keep this technique in mind but it might be that I’m already accidentally doing this.
Actually JonnieDie is a good friend of mine and he recommended this to me a long time ago for something else. This might a really good use case for it, though I’m very grateful for the other recommendations in the thread to help with my Julia education!
For my specific case, T just ends up being AbstractFloat so I can switch bit sizes, which I do utilize. In my example with q, q is a vector of SVectors, but the SVector elements can be arbitrary sizes for a given system config. It just depends on the inputs what the size of each element will be. I have other fields that will be Vectors of Matrices, or even Vectors of other MyTypes.
There are other ways for me to define those structures technically but I can stay very close to the textbook notation by writing it this way, which I think is very cool that Julia can do!
What I actually meant was that T could represent your entire SArray{...}. If T specifically is currently used for a number type, replace the parameter name with S or R or any other name.