- It's easy to create an array of functions and execute them in a loop.
- It's easy to provide arguments in either a corresponding array of the same length or the array could be of tuples (fn, arg).
For 2, the loop is just
for fn_ar in arr # arr is [(myfunc, [1,2,3]), (func2, [10,11,12]), ...]
fn_ar[1](fn_ar[2])
end
Here is the problem: the arguments I am using are arrays of very large arrays. In #2, the argument that will be called with the function will be the current value of the array when the arg entry of the tuple is initially created. What I need is to provide the array names as the argument and defer evaluation of the arguments until the corresponding function is run in the loop body.
I could provide the arrays used as input as an expression and eval the expression in the loop to supply the needed arguments. But, eval can't eval in local scope.
What I did that worked (sort of) was to create a closure for each function that captured the arrays (which are really just a reference to storage). This works because the only argument to each function that varies in the loop body turns out to be the loop counter. The functions in question update the arrays in place. The array argument is really just a reference to the storage location, so each function executed in the loop body sees the latest values of the arrays. It worked. It wasn't hard to do. It is very, very slow. This is a known challenge in Julia.
I tried the recommended hints in the performance section of the manual. Make sure the captured variables are typed before they are captured so the JIT knows what they are. No effect on perf. The other hint is to put the definition of the curried function with the data for the closure in let block. Tried this. No effect on perf. It's possible I implemented the hints incorrectly--I can provide a code fragment if it helps.
But, I'd rather just ask the question about what I am trying to do and not muddy the waters with my past effort, which might not be going down the right path.
Here is a small fragment that is more realistic than the above:
Just a couple of functions and arguments:
(affine!, "(dat.z[hl], dat.a[hl-1], nnw.theta[hl], nnw.bias[hl])")
(relu!, "(dat.a[hl], dat.z[hl])")
Of course, the arguments could be wrapped as an expression with Meta.parse. dat.z and dat.a are matrices used in machine learning. hl indexes the layer of the model for the linear result and non-linear activation.
A simplified version of the loop where I want to run through the stack of functions across the model layers:
function feedfwd!(dat::Union{Batch_view,Model_data}, nnw, hp, ff_execstack)
for lr in 1:hp.n_layers
for f in ff_execstack[lr]
f(lr)
end
end
end
So, closures of the arrays is too slow. Eval I can't get to work.
Any suggestions...?
Thanks, Lewis
f(dat, lr)then f can modify dat inplace which should resolve the efficiency issue.