Technical stuff - achilleas-k/BrianDROID GitHub Wiki
This page lists and describes the reasons behind the most important implementation choices.
Renderscript
BrianDROID is all about heavy computation, so it was clear from the start that a high performance language/engine would benefit the project greatly. The two solutions available to Android developers are the NDK and RenderScript. Mostly due to time constraints, experimenting with both was not feasible and the decision to use RenderScript was made based on the suggested use cases for each. In particular, RenderScript provides a straightforward interface for computing in parallel, which resulted in a significant performance boost for this application.
Dynamic arrays
Dynamic arrays are required in BrianDROID in order to handle several objects, namely spike monitors, state monitors and synapses. Although only one of the three aforementioned objects is currently implemented - spike monitors - we have considered several ways for implementing the others that may be more convenient or efficient. Furthermore, given that in some cases it may be beneficial, or required, to use dynamic arrays in the RenderScript engine, the current implementation may change.
Spike monitor
Currently, spike monitors are handled entirely in Java code.
By default, whether a spike monitor is defined or not, for each threshold, the code generation module uses the spikespace
array to track the index numbers of neurons that spike at each timestep and handle the reset and refractoriness (if necessary).
While spikespace
is handled in RenderScript, the spike monitor reads the indices in the array at each time step and appends them to a SpikeMonitor
object, which essentially contains an ArrayList
of index-time pairs.
This implementation was chosen to take advantage of the strengths of Java, which provides convenient and efficient dynamic memory allocation. However, alternatives will be experimented with in the future to find more efficient implementations.
Alternatives
Alternative implementations could pre-allocate memory to arrays available in the RenderScript engine and handle reallocations when necessary. This might be especially useful for building the synapses, since the exact number cannot be known beforehand and there is practically no upper bound. Descriptions of several alternative implementations can be found below:
- A dynamic array can be mirrored statically in RenderScript. The RenderScript code can check the available space in the array and message Java when more is required. The array will then be reallocated by Java and rebound to RenderScript.
- The same as above, but instead of RenderScript checking for available space, it can be done on the Java side.
- Statically with synapse array size calculated in Python during code generation. For state monitors, a static array may be implemented regardless of the way synapses are handled, the size being
N*T/dt
, (number of neurons * duration / timestep). For synapses the final size is much less trivial to calculate. Synapses can be defined in a multitude of ways, some of which are probabilistic (e.g., "0.4 connectivity"). In such cases, we could set up a connectivity matrix in Python during code generation and generate the code accordingly. However, this would mean that random connectivity would be fixed across multiple runs of the same simulation on Android, where the code is never changed. Alternatively, we could handle synapse creation completely on the Java side.
Dynamic class loading
BrainDROID was originally planned to operate as a classloader. The general idea was that it would allow us to distribute a complete app through the Android Play Store which would expose device-specific functionality to dynamically loaded class files. On the user side, this would simplify the usage greatly. Users would be able to compile the files that are automatically generated by Brian2 using a relatively simple script we would distribute with the library and load it onto their devices without requiring a complete Android build environment.
However, this decision had to be abandoned to allow for running the computationally demanding state update equations in RenderScript. Bitcode and other raw resources must be part of the .apk file for it to run and the auto-generated ScriptC_renderscript class needs access to the calling application's resources at build time.