Why do you want to have an array at all? As far as I understood, the 30-40 functions don’t change from person to person. Hence, you can just write 30-40 lines of source code (you do need to define each of these functions anyways).
An alternative would be to go metaprogramming: Either by using a macro or a generated function. In that case, you could write one big
funs = global_fun_dict[S]
#emit code calling funs...
end
Then you would maintain one big global Dict{Any,Vector{Any}}. This Dict would only be queried at compile time, so does not need to be typed.
FunctionWrappers should probably do the job as well.
Thanks. My goal, which until now I had thought was probably easy to accomplish, is for the list of functions and their ordering to be specified in a json file:
read the json file to get the names of the functions
use getfield to put them into a tuple or static array
loop through the data records
for each record, loop through the functions and call them
etc.
Because I know how to do 1 & 2, my example focused on doing 3 & 4 in combination, with a loop, without a performance penalty. It sounds like doing that is not easy. I’ll read about FunctionWrappers.
Yeah, if the set of functions you want to run is only known at run-time (when you read the .json file), then FunctionWrappers is probably a good choice. FunctionWrappers should allow you to loop over a vector of functions (provided all of them have the same signature) with little to no performance penalty.
Many thanks, all. I am new to Julia and not a programmer by trade so it is a steep learning curve. I do now have a solution that appears to work (below) and am investigating others.
I have started to investigate the Dict approach mentioned above. My early efforts found that iterating through a Dict produces the same memory allocation as looping through arrays or tuples, but I did not use metaprogramming. I am reading about FunctionWrappers but don’t fully understand it yet. The “using type for dispatch” and the “tuple of structs” approaches mentioned above sound like they would provide maximum flexibility and I will learn how to use them.
As for the short term, what I have learned is that unrolling the inner loop (as suggested by the timings above) avoids the large excessive memory allocations that looping through a tuple or array requires. The code below does this, and runs a fair amount faster (1/3 time) than the not-unrolled loop, in my simple example of looping through only 2 functions. It also does not require a lot of hard (for me) to read code. So I have a workable solution I can use while I learn more about Julia.
using Unrolled
const cfuns = (sin, cos)
@unroll function test_loop(n::Int64, cfuns::Tuple)
t = 0.0
for i = 1:n
@unroll for j in 1:length(cfuns)
t += cfuns[j](i)
end
end
return t
end
@time test_loop(10^6, cfuns) # 20.1k allocations
@time test_loop(10^6, cfuns) # 6 allocations