Motivation
Following from discussion starting here: #1042 (comment)
With contemporary systems, moving the data from memory into caches and registers often dominates the runtime of numerical algorithms depending on how many arithmetic operations they perform relative to each memory access. Fusing operations from multiple passes into a single pass increases this ratio and thereby the likelihood of efficient CPU utilisation. [1]
In theory this problem is solved using loops and/or mapv, but this doesn't provide a very user-friendly interface. Users who want to work at a high level still want to be able to use arr.pow(), arr.log() etc, but also retain the speed of this per-element processing (rather than applying each operation to each element and then the next operation to each element).
Proposed Changes
- We move a subset of the
ArrayBase methods into an ArrayOps trait. Note: this probably can't be all methods because it can only work for methods that return an array, so axes, as_slice, get etc don't necessarily make sense
- This trait gets implemented by
ArrayBase, but also a new struct called LazyChain (or something idk)
LazyChain gets created by ArrayBase.chain()
LazyChain will own (?) an ArrayBase, as well as a list of operations that will be sequentially applied to it
- Then,
LazyChain.eval() runs these operations sequentially on each element of the owned array, and then returns the modified array
Decision Points
- Names for structs and traits
- Should
LazyChain return a copy or a modified in-place array?
- Should
LazyChain own the array or just borrow it? Should we have two different types do each?
Motivation
Following from discussion starting here: #1042 (comment)
In theory this problem is solved using loops and/or
mapv, but this doesn't provide a very user-friendly interface. Users who want to work at a high level still want to be able to usearr.pow(),arr.log()etc, but also retain the speed of this per-element processing (rather than applying each operation to each element and then the next operation to each element).Proposed Changes
ArrayBasemethods into anArrayOpstrait. Note: this probably can't be all methods because it can only work for methods that return an array, soaxes,as_slice,getetc don't necessarily make senseArrayBase, but also a new struct calledLazyChain(or something idk)LazyChaingets created byArrayBase.chain()LazyChainwill own (?) anArrayBase, as well as a list of operations that will be sequentially applied to itLazyChain.eval()runs these operations sequentially on each element of the owned array, and then returns the modified arrayDecision Points
LazyChainreturn a copy or a modified in-place array?LazyChainown the array or just borrow it? Should we have two different types do each?