<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE article PUBLIC "-//NLM//DTD Journal Publishing DTD v2.3 20070202//EN" "journalpublishing.dtd">
<article xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" article-type="research-article">
<front>
<journal-meta>
<journal-id journal-id-type="publisher-id">Front. Neuroinform.</journal-id>
<journal-title>Frontiers in Neuroinformatics</journal-title>
<abbrev-journal-title abbrev-type="pubmed">Front. Neuroinform.</abbrev-journal-title>
<issn pub-type="epub">1662-5196</issn>
<publisher>
<publisher-name>Frontiers Media S.A.</publisher-name>
</publisher>
</journal-meta>
<article-meta>
<article-id pub-id-type="doi">10.3389/fninf.2014.00078</article-id>
<article-categories>
<subj-group subj-group-type="heading">
<subject>Neuroscience</subject>
<subj-group>
<subject>Original Research Article</subject>
</subj-group>
</subj-group>
</article-categories>
<title-group>
<article-title>Spiking network simulation code for petascale computers</article-title>
</title-group>
<contrib-group>
<contrib contrib-type="author" corresp="yes">
<name><surname>Kunkel</surname> <given-names>Susanne</given-names></name>
<xref ref-type="aff" rid="aff1"><sup>1</sup></xref>
<xref ref-type="aff" rid="aff2"><sup>2</sup></xref>
<xref ref-type="author-notes" rid="fn001"><sup>&#x0002A;</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/8419"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Schmidt</surname> <given-names>Maximilian</given-names></name>
<xref ref-type="aff" rid="aff3"><sup>3</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/174360"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Eppler</surname> <given-names>Jochen M.</given-names></name>
<xref ref-type="aff" rid="aff3"><sup>3</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/2466"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Plesser</surname> <given-names>Hans E.</given-names></name>
<xref ref-type="aff" rid="aff3"><sup>3</sup></xref>
<xref ref-type="aff" rid="aff4"><sup>4</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/2833"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Masumoto</surname> <given-names>Gen</given-names></name>
<xref ref-type="aff" rid="aff5"><sup>5</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/60300"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Igarashi</surname> <given-names>Jun</given-names></name>
<xref ref-type="aff" rid="aff6"><sup>6</sup></xref>
<xref ref-type="aff" rid="aff7"><sup>7</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/61259"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Ishii</surname> <given-names>Shin</given-names></name>
<xref ref-type="aff" rid="aff8"><sup>8</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/70366"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Fukai</surname> <given-names>Tomoki</given-names></name>
<xref ref-type="aff" rid="aff7"><sup>7</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/22174"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Morrison</surname> <given-names>Abigail</given-names></name>
<xref ref-type="aff" rid="aff1"><sup>1</sup></xref>
<xref ref-type="aff" rid="aff3"><sup>3</sup></xref>
<xref ref-type="aff" rid="aff9"><sup>9</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/13504"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Diesmann</surname> <given-names>Markus</given-names></name>
<xref ref-type="aff" rid="aff3"><sup>3</sup></xref>
<xref ref-type="aff" rid="aff7"><sup>7</sup></xref>
<xref ref-type="aff" rid="aff10"><sup>10</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/630"/>
</contrib>
<contrib contrib-type="author">
<name><surname>Helias</surname> <given-names>Moritz</given-names></name>
<xref ref-type="aff" rid="aff2"><sup>2</sup></xref>
<xref ref-type="aff" rid="aff3"><sup>3</sup></xref>
<uri xlink:href="http://community.frontiersin.org/people/u/2031"/>
</contrib>
</contrib-group>
<aff id="aff1"><sup>1</sup><institution>Simulation Laboratory Neuroscience &#x02013; Bernstein Facility for Simulation and Database Technology, Institute for Advanced Simulation, J&#x000FC;lich Aachen Research Alliance, J&#x000FC;lich Research Centre</institution> <country>J&#x000FC;lich, Germany</country></aff>
<aff id="aff2"><sup>2</sup><institution>Programming Environment Research Team, RIKEN Advanced Institute for Computational Science</institution> <country>Kobe, Japan</country></aff>
<aff id="aff3"><sup>3</sup><institution>Institute of Neuroscience and Medicine (INM-6), Institute for Advanced Simulation (IAS-6), J&#x000FC;lich Research Centre and JARA</institution> <country>J&#x000FC;lich, Germany</country></aff>
<aff id="aff4"><sup>4</sup><institution>Department of Mathematical Sciences and Technology, Norwegian University of Life Sciences</institution> <country>Aas, Norway</country></aff>
<aff id="aff5"><sup>5</sup><institution>Advanced Center for Computing and Communication, RIKEN</institution> <country>Wako, Japan</country></aff>
<aff id="aff6"><sup>6</sup><institution>Neural Computation Unit, Okinawa Institute of Science and Technology</institution> <country>Okinawa, Japan</country></aff>
<aff id="aff7"><sup>7</sup><institution>Laboratory for Neural Circuit Theory, RIKEN Brain Science Institute</institution> <country>Wako, Japan</country></aff>
<aff id="aff8"><sup>8</sup><institution>Integrated Systems Biology Laboratory, Department of Systems Science, Graduate School of Informatics, Kyoto University</institution> <country>Kyoto, Japan</country></aff>
<aff id="aff9"><sup>9</sup><institution>Faculty of Psychology, Institute of Cognitive Neuroscience, Ruhr-University Bochum</institution> <country>Bochum, Germany</country></aff>
<aff id="aff10"><sup>10</sup><institution>Medical Faculty, RWTH University</institution> <country>Aachen, Germany</country></aff>
<author-notes>
<fn fn-type="edited-by"><p>Edited by: Anders Lansner, Royal Institute of Technology (KTH), Sweden</p></fn>
<fn fn-type="edited-by"><p>Reviewed by: Thomas Natschl&#x000E4;ger, Software Competence Center Hagenberg GmbH, Austria; James Kozloski, IBM Thomas J. Watson Research Center, Yorktown Heights, USA; Frederick C. Harris, University of Nevada, Reno, USA</p></fn>
<fn fn-type="corresp" id="fn001"><p>&#x0002A;Correspondence: Susanne Kunkel, Forschungszentrum J&#x000FC;lich GmbH, JSC, 52425 J&#x000FC;lich, Germany e-mail: <email>kunkel&#x00040;fz-juelich.de</email></p></fn>
<fn fn-type="other" id="fn002"><p>This article was submitted to the journal Frontiers in Neuroinformatics.</p></fn>
</author-notes>
<pub-date pub-type="epreprint">
<day>18</day>
<month>07</month>
<year>2014</year>
</pub-date>
<pub-date pub-type="epub">
<day>10</day>
<month>10</month>
<year>2014</year>
</pub-date>
<pub-date pub-type="collection">
<year>2014</year>
</pub-date>
<volume>8</volume>
<elocation-id>78</elocation-id>
<history>
<date date-type="received">
<day>18</day>
<month>06</month>
<year>2014</year>
</date>
<date date-type="accepted">
<day>27</day>
<month>08</month>
<year>2014</year>
</date>
</history>
<permissions>
<copyright-statement>Copyright &#x000A9; 2014 Kunkel, Schmidt, Eppler, Plesser, Masumoto, Igarashi, Ishii, Fukai, Morrison, Diesmann and Helias.</copyright-statement>
<copyright-year>2014</copyright-year>
<license license-type="open-access" xlink:href="http://creativecommons.org/licenses/by/4.0/"><p>This is an open-access article distributed under the terms of the Creative Commons Attribution License (CC BY). The use, distribution or reproduction in other forums is permitted, provided the original author(s) or licensor are credited and that the original publication in this journal is cited, in accordance with accepted academic practice. No use, distribution or reproduction is permitted which does not comply with these terms.</p>
</license>
</permissions>
<abstract><p>Brain-scale networks exhibit a breathtaking heterogeneity in the dynamical properties and parameters of their constituents. At cellular resolution, the entities of theory are neurons and synapses and over the past decade researchers have learned to manage the heterogeneity of neurons and synapses with efficient data structures. Already early parallel simulation codes stored synapses in a distributed fashion such that a synapse solely consumes memory on the compute node harboring the target neuron. As petaflop computers with some 100,000 nodes become increasingly available for neuroscience, new challenges arise for neuronal network simulation software: Each neuron contacts on the order of 10,000 other neurons and thus has targets only on a fraction of all compute nodes; furthermore, for any given source neuron, at most a single synapse is typically created on any compute node. From the viewpoint of an individual compute node, the heterogeneity in the synaptic target lists thus collapses along two dimensions: the dimension of the types of synapses and the dimension of the number of synapses of a given type. Here we present a data structure taking advantage of this double collapse using metaprogramming techniques. After introducing the relevant scaling scenario for brain-scale simulations, we quantitatively discuss the performance on two supercomputers. We show that the novel architecture scales to the largest petascale supercomputers available today.</p></abstract>
<kwd-group>
<kwd>supercomputer</kwd>
<kwd>large-scale simulation</kwd>
<kwd>parallel computing</kwd>
<kwd>computational neuroscience</kwd>
<kwd>memory footprint</kwd>
<kwd>memory management</kwd>
<kwd>metaprogramming</kwd>
</kwd-group>
<counts>
<fig-count count="11"/>
<table-count count="1"/>
<equation-count count="18"/>
<ref-count count="47"/>
<page-count count="23"/>
<word-count count="18197"/>
</counts>
</article-meta>
</front>
<body>
<sec sec-type="introduction" id="s1">
<title>1. Introduction</title>
<p>In the past decade, major advances have been made that allow the routine simulation of spiking neuronal networks on the scale of the local cortical volume, i.e., containing up to 10<sup>5</sup> neurons and 10<sup>9</sup> synapses, including the exploitation of distributed and concurrent computing, the incorporation of experimentally observed phenomena such as plasticity, and the provision of appropriate high-level user interfaces (Davison et al., <xref ref-type="bibr" rid="B9">2008</xref>; Bednar, <xref ref-type="bibr" rid="B5">2009</xref>; Eppler et al., <xref ref-type="bibr" rid="B14">2009</xref>; Hines et al., <xref ref-type="bibr" rid="B22">2009</xref>; Goodman and Brette, <xref ref-type="bibr" rid="B18">2013</xref>; Zaytsev and Morrison, <xref ref-type="bibr" rid="B47">2014</xref>). However, such models are intrinsically limited. Firstly, a cortical neuron receives only about 50% of its synaptic inputs from neurons in the same cortical volume; in such models, the other half is generally abstracted as a random or constant input. Consequently, larger models are required to arrive at self-consistent descriptions. Secondly, many cognitive tasks involve the co-ordinated activity of multiple brain areas. Thus, in order to understand such processes, it is necessary to develop and investigate models on the brain scale.</p>
<p>In a previous study (Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>), we presented data structures that allow the neuronal network simulator NEST (Gewaltig and Diesmann, <xref ref-type="bibr" rid="B17">2007</xref>) to exploit the increasingly available supercomputers such as JUQUEEN and the K computer (Helias et al., <xref ref-type="bibr" rid="B21">2012</xref>). Although we could carry out benchmarks utilizing over 100,000 cores, analysis of the memory consumption (Section 3.1) reveals that at such large sizes, the infrastructure required on each machine to store synapses with local targets becomes the dominant component.</p>
<p>The reason can be understood with a simple calculation. Assuming each neuron contacts 10,000 other neurons, and that these neurons are randomly distributed over the entire supercomputer, then on a system with 100,000 cores, the probability of a core containing more than one target neuron is rather small, and the majority of cores will contain no target neurons of the given neuron. This is illustrated in Figure <xref ref-type="fig" rid="F1">1</xref>; as the total network size increases (whilst maintaining a constant number of neurons on a core), the number of target lists of non-zero length approaches the number of synapses on the local core and, consequently, each target list has an expected length of 1. As each new target list comes with a certain memory overhead, the average total costs per synapse increase with increasing network size. This acceleration in memory consumption only stops when each target list is either of length 0 or length 1 and from then on each synapse carries the full overhead of one target list. With realistic parameters, the largest networks that can be represented on supercomputers using the technology employed in Helias et al. (<xref ref-type="bibr" rid="B21">2012</xref>) reach this limit.</p>
<fig id="F1" position="float">
<label>Figure 1</label>
<caption><p><bold>Number of local target lists approaches the number of local synapses</bold>. The gray curve shows the expected number of target lists per core <italic>N</italic><sup>&#x0003E;0</sup><sub>c</sub>, given by Equation (6), that contain at least one synapse as a function of the total network size <italic>N</italic>. The pink curve (1 &#x02212; exp(&#x02212; <italic>K</italic><sub>VP</sub>/<italic>N</italic>))<italic>N</italic> is an approximation (7) for large networks. Here, each core represents <italic>N</italic><sub>VP</sub> &#x0003D; 2000 neurons with <italic>K</italic> &#x0003D; 10,000 synapses per neuron, which is a realistic example. In the limit of large <italic>N</italic> the number of target lists approaches the number of local synapses <italic>K</italic><sub>VP</sub> &#x0003D; <italic>K</italic><sub>VP</sub> (black dashed horizontal line). The gray vertical line marks the network size <italic>N</italic><sub>&#x003B6;</sub>, given by Equation (9), at which the number of target lists has reached &#x003B6; &#x0003D; 99% of the limit <italic>K</italic><sub>VP</sub>. The pink dashed vertical line at <italic>K</italic><sub>VP</sub>/2/(1 &#x02212; &#x003B6;) is an approximation (10) of the full expression (9).</p></caption>
<graphic xlink:href="fninf-08-00078-g0001.tif"/>
</fig>
<p>The memory model introduced in Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>) triggered the major advance of using sparse tables to store incoming connections, thus reducing the memory overhead for target lists of length 0. However, the memory overhead for neurons with local targets is still substantial, as the data structure is designed to enable variable amounts of heterogeneous connection types, e.g., static synapses and various types of plasticity including short-term plasticity (Tsodyks et al., <xref ref-type="bibr" rid="B42">1998</xref>, <xref ref-type="bibr" rid="B43">2000</xref>) and spike-timing dependent plasticity (see e.g., (Morrison et al., <xref ref-type="bibr" rid="B33">2008</xref>), for a review). Nevertheless, as the network size increases, it becomes increasingly common that a neuron has only one local target, thus the majority of this structure is redundant: both the number (just 1) and the type of the connection are known.</p>
<p>Thus, the challenge is to develop a data structure that is streamlined for the most common case (on large systems) that a neuron has one local target, and yet allows the full flexibility with respect to connection type and number when needed. A constraint on the granularity of the parallelization is the assumption that the neuron objects themselves are not distributed but viewed as atomistic and simulated on a single compute node as an entity. Consequently the connection infrastructure only needs to represent synaptic connections, not connections between different compartments of a neuron. In Section 3.2, we present a data structure that fulfills these criteria by self-collapsing along two dimensions: the heterogeneity collapses to a single well-defined connection type and the dynamic length of the connections vector collapses to a single element. Moreover, a redesign of the synapse data structure and the handshaking algorithm at connection time (see Section 3.3) allow the polymorphism of the synapse types to be exploited without requiring a pointer to a virtual function table, thus saving 8 B for every synapse in the network. Making use of the limited number of neurons local to a core further allows us to replace the full 8 B target pointer by a 2 B index and combining the delay and the synapse type into a joint data structure saves another 8 B. The creation of many small synapse objects presents a challenge to the memory allocation which we meet by implementing a dedicated pool allocator in Section 3.4. Finally, in Section 3.5 a new data structure to store local neurons is introduced. The sparse node array exploits the regularity in assigning neurons to machines and thereby eliminates memory overhead for non-local neurons at the expense of an increased number of search steps to locate a node. The major result of this study is that we are now capable of employing the full size of currently available petascale computers such as JUQUEEN and K.</p>
<p>In Section 2.1 we describe the basic characteristics of the software environment under study and Section 2.2 specifies the neuronal network model used to obtain quantitative data. Section 2.3 extends the mathematical memory model previously introduced in Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>) which we use to analyze the scaling properties of alternative data structures.</p>
<p>In Section 3.1 we first investigate the memory consumption of NEST on petascale computers in detail. In the following sections we describe the data structures, corresponding algorithms, and the allocator outlined above. Finally in Section 3.6 we quantitatively compare the resulting new (4g) simulation code with the previous (3g) version on the two supercomputers JUQUEEN and K. The capability of the fourth generation code is demonstrated by orchestrating all the available main memory of the K computer in a single simulation. In the concluding section we assess our achievements in terms of flexibility and network size in the light of previous work and discuss limitations.</p>
<p>This article concludes a co-development project for the K computer in Kobe, which started in 2008 (Diesmann, <xref ref-type="bibr" rid="B11">2013</xref>). Preliminary results have been published in abstract form (Diesmann, <xref ref-type="bibr" rid="B10">2012</xref>; Kunkel et al., <xref ref-type="bibr" rid="B30">2013</xref>) and as a joint press release of the J&#x000FC;lich Research Centre and RIKEN (RIKEN BSI, <xref ref-type="bibr" rid="B39">2013</xref>). The conceptual and algorithmic work described here is a module in our long-term collaborative project to provide the technology for neural systems simulations (Gewaltig and Diesmann, <xref ref-type="bibr" rid="B17">2007</xref>).</p>
</sec>
<sec sec-type="materials and methods" id="s2">
<title>2. Materials and methods</title>
<sec>
<title>2.1. NEST simulator</title>
<p>NEST is a simulation software for spiking neuronal networks of single- and few-compartment neuron models (Gewaltig and Diesmann, <xref ref-type="bibr" rid="B17">2007</xref>). It incorporates a number of technologies for the accurate and efficient simulation of neuronal systems such as the exact integration of models with linear subthreshold dynamics (Rotter and Diesmann, <xref ref-type="bibr" rid="B40">1999</xref>), algorithms for synaptic plasticity (Morrison et al., <xref ref-type="bibr" rid="B32">2007</xref>, <xref ref-type="bibr" rid="B33">2008</xref>; Potjans et al., <xref ref-type="bibr" rid="B37">2010</xref>), the framework for off-grid spiking including synaptic delays (Morrison et al., <xref ref-type="bibr" rid="B34">2005a</xref>; Hanuschkin et al., <xref ref-type="bibr" rid="B19">2010</xref>) and the Python-based user interface PyNEST/CyNEST (Eppler et al., <xref ref-type="bibr" rid="B14">2009</xref>; Zaytsev and Morrison, <xref ref-type="bibr" rid="B47">2014</xref>). NEST is developed by the NEST Initiative and available under the GNU General Public License. It can be downloaded from the website of the NEST Simulator (<ext-link ext-link-type="uri" xlink:href="http://www.nest-simulator.org">http://www.nest-simulator.org</ext-link>).</p>
<p>NEST uses a hybrid parallelization strategy during the setup and simulation phase with one MPI process per compute node and multi-threading based on OpenMP within each process. The use of threads instead of MPI processes on the cores is the basis of light-weight parallelization, because process-based distribution employed in MPI enforces the replication of the entire application on each MPI process and process management entails additional overhead (Eppler et al., <xref ref-type="bibr" rid="B12">2007</xref>; Plesser et al., <xref ref-type="bibr" rid="B36">2007</xref>). Thread-parallel components do not require the duplication of data structures <italic>per se</italic>. However, the current implementation of NEST duplicates parts of the connection infrastructure for each thread to achieve good cache performance during the thread-parallel delivery of spike events to the target neurons.</p>
<p>Furthermore, on a given supercomputer the number of MPI processes may have limits; on K, for example, there can be only one MPI job per node and the total number of MPI jobs is limited to 88,128. The neurons of the network are evenly distributed over the compute nodes in a round-robin fashion and communication between machines is performed by collective MPI functions (Eppler et al., <xref ref-type="bibr" rid="B12">2007</xref>). The delivery of a spike event from a given neuron to its targets requires that each receiving machine has the information available to determine whether the sending neuron has any targets local to that machine. In NEST, this is realized by storing the outgoing synapses to local targets in a data structure logically forming a target list. For the 3rd generation kernel, this data structure is described in detail in Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>) and for the 4th generation kernel in Section 3.2. For comparison, the two data structures are illustrated in Figure <bold>3</bold>. In addition, the memory consumption caused by the currently employed collective data exchange scheme (<monospace>MPI_Allgather</monospace>) increases with the number of MPI processes.</p>
</sec>
<sec>
<title>2.2. Network model</title>
<p>All measurements of memory usage and run time are carried out for a balanced random network model (Brunel, <xref ref-type="bibr" rid="B7">2000</xref>) of 80% excitatory and 20% inhibitory integrate-and-fire neurons with alpha-shaped post-synaptic currents. Both types of neurons are represented by the NEST model <monospace>iaf_neuron</monospace> with a homogeneous set of parameters. All excitatory-excitatory connections exhibit spike-timing dependent plasticity (STDP) and all other connections are static. Simulations performed with the 3rd generation simulation kernel (Helias et al., <xref ref-type="bibr" rid="B21">2012</xref>; Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>) employ the models <monospace>stdp_pl_synapse_hom</monospace> and <monospace>static_synapse</monospace> whereas simulations run with the 4th generation simulation kernel presented in this manuscript use the novel high-performance computing (HPC) versions of these models (<monospace>stdp_pl_synapse_hom_hpc</monospace> and <monospace>static_synapse_hpc</monospace>, described in Section 3.3). We use two sets of parameters for the benchmarks. Within each set, the only parameter varied is the network size in terms of number of neurons <italic>N</italic>.</p>
<list list-type="simple">
<list-item><p><bold>Set 1</bold> The total number of incoming connections per neuron is fixed at <italic>K</italic> &#x0003D; 11,250 (9000 excitatory, 2250 inhibitory). The initial membrane potentials are drawn from a normal distribution with &#x003BC; &#x0003D; 9.5 mV and &#x003C3; &#x0003D; 5.0 mV. The initial synaptic weights are set to <italic>J</italic><sub><italic>E</italic></sub> &#x0003D; 45.61 pA for excitatory and to <italic>J</italic><sub><italic>I</italic></sub> &#x0003D; &#x02212;<italic>gJ</italic><sub><italic>E</italic></sub>, <italic>g</italic> &#x0003D; 5 for inhibitory synapses. All neurons receive excitatory external Poissonian input causing a mean membrane potential of &#x003B7; <inline-formula><mml:math id="M1"><mml:mrow><mml:msub><mml:mi>V</mml:mi><mml:mrow><mml:mtext>th</mml:mtext></mml:mrow></mml:msub><mml:mo>=</mml:mo><mml:msub><mml:mi>&#x003C4;</mml:mi><mml:mrow><mml:mtext>syn</mml:mtext></mml:mrow></mml:msub><mml:msub><mml:mi>J</mml:mi><mml:mi>E</mml:mi></mml:msub><mml:mfrac><mml:mrow><mml:msub><mml:mi>&#x003C4;</mml:mi><mml:mi>m</mml:mi></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>C</mml:mi><mml:mi>m</mml:mi></mml:msub></mml:mrow></mml:mfrac><mml:msub><mml:mi>&#x003BD;</mml:mi><mml:mrow><mml:mtext>ext</mml:mtext><mml:mo>.</mml:mo></mml:mrow></mml:msub></mml:mrow></mml:math></inline-formula>. With &#x003B7; &#x0003D; 1.685, <italic>V</italic><sub>th</sub> &#x0003D; 20 mV, &#x003C4;<sub><italic>m</italic></sub> &#x0003D; 10 ms, <italic>C</italic><sub><italic>m</italic></sub> &#x0003D; 250 pF, and &#x003C4;<sub>syn</sub> &#x0003D; 0.3258 ms, this corresponds to the input spike rate of <inline-formula><mml:math id="M2"><mml:mrow><mml:msub><mml:mi>&#x003BD;</mml:mi><mml:mrow><mml:mtext>ext</mml:mtext><mml:mo>.</mml:mo></mml:mrow></mml:msub><mml:mo>=</mml:mo><mml:mi>&#x003B7;</mml:mi><mml:mfrac><mml:mrow><mml:msub><mml:mi>V</mml:mi><mml:mrow><mml:mtext>th</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>&#x003C4;</mml:mi><mml:mrow><mml:mtext>syn</mml:mtext></mml:mrow></mml:msub><mml:msub><mml:mi>J</mml:mi><mml:mi>E</mml:mi></mml:msub><mml:mfrac><mml:mrow><mml:msub><mml:mi>&#x003C4;</mml:mi><mml:mi>m</mml:mi></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>C</mml:mi><mml:mi>m</mml:mi></mml:msub></mml:mrow></mml:mfrac></mml:mrow></mml:mfrac><mml:mo>&#x02243;</mml:mo><mml:mn>20</mml:mn><mml:mo>,</mml:mo><mml:mn>856</mml:mn></mml:mrow></mml:math></inline-formula> spikes per second summed over all external inputs to a neuron. Within the simulation period of 1 s, each neuron fires on average 7.6 times. Spikes are communicated every 1.5 ms, corresponding to the synaptic delay, but neuronal state variables are advanced in steps of 0.1 ms. For further details of the network model such as neuronal and synaptic parameters please see the example script <monospace>hpc_benchmark.sli</monospace>, which is available in the next major release of NEST.</p></list-item>
<list-item><p><bold>Set 2</bold> For the second set of benchmarks, the number of incoming connections per neurons is reduced to <italic>K</italic> &#x0003D; 6000. The other parameters are adapted to obtain an irregular activity state with an average rate of 4.5 spikes per second. The adapted parameters are <italic>J</italic><sub><italic>E</italic></sub> &#x0003D; 50 pA, <italic>g</italic> &#x0003D; 7, and &#x003B7; &#x0003D; 1.2. All other parameters are the same as in set 1.</p></list-item>
</list>
</sec>
<sec>
<title>2.3. Memory-usage model</title>
<p>Our efforts to redesign the objects and fundamental data structures of NEST toward ever more scalable memory usage are guided by the method introduced in Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>). The method is based on a model which describes the memory usage of a neuronal network simulator as a function of the network parameters, i.e., the number of neurons <italic>N</italic> and the number <italic>K</italic> of synapses per neuron as well as the parameters characterizing the distribution of the simulation code over the machine, i.e., the number of compute nodes <italic>M</italic> and the number of threads <italic>T</italic> running on each compute node. Threads are also termed &#x0201C;virtual processes&#x0201D; due to NEST&#x00027;s internal treatment of threads as if they were MPI processes by completely separating their memory areas. This replication of data structures for each thread is reflected in expressions of the memory consumption of the synaptic data structures that depend only on the product <italic>MT</italic>, as shown in Section 2.4. In the following, we therefore use the terms &#x0201C;the total number of virtual processes&#x0201D; synonymously with &#x0201C;the total number of threads,&#x0201D; both referring to <italic>MT</italic>. We apply the model to NEST to determine the data structures that dominate the total memory usage at a particular target regime of number of virtual processes. Once the critical parts of NEST have been identified, the model enables us to predict the effect of potential design changes for the entire range of the total number of threads from laptops to supercomputers. Furthermore, the model assists the benchmarking process as it facilitates the estimation of the maximum network size that just fits on a given number of compute nodes using a given number of threads each. We briefly restate the model here and describe the required alterations to the model for the petascale regime, which allow a more precise assessment of the contributions of different parts of infrastructure. For further details on the model and its practical application, please refer to our previous publications (Helias et al., <xref ref-type="bibr" rid="B21">2012</xref>; Kunkel et al., <xref ref-type="bibr" rid="B28">2012a</xref>,<xref ref-type="bibr" rid="B29">b</xref>).</p>
<p>Three main components contribute to the total memory consumption of a neuronal network simulator: the base memory usage of the simulator including external libraries such as MPI, <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>0</sub> (<italic>M</italic>), the additional memory usage that accrues when neurons are created, <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>n</sub> (<italic>M</italic>, <italic>N</italic>), and the additional memory usage that accrues when neurons are connected, <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>c</sub> (<italic>M</italic>, <italic>T</italic>, <italic>N</italic>, <italic>K</italic>). The memory consumption per MPI process is given by</p>
<graphic xlink:href="fninf-08-00078-i0002.tif"/>
<p>As suggested in Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>), we determined <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>0</sub> (<italic>M</italic>) by measuring the memory usage of NEST right after start-up, which was at most 268 MB on the K computer and 26 MB on JUQUEEN. However, in this study <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>0</sub> (<italic>M</italic>) also accounts for the communication buffer that each MPI process requires in order to receive spike information from other processes. As NEST uses <monospace>MPI_Allgather</monospace> to communicate spike data, the buffer grows proportionally with the number of MPI processes <italic>M</italic>. Hence, in the petascale regime the contribution of this buffer to the total memory usage is no longer negligible. Here, we assume that each MPI process maintains an outgoing buffer of size 1000, where each entry consumes 4 B, such that the memory that is taken up by the incoming buffer amounts to <italic>M</italic> &#x000D7; 4 kB. In NEST the communication buffers increase dynamically whenever the instantaneous rate of the simulated network requires more spikes to be communicated. In simulations of the benchmark network model described in Section 2.2 we measured send-buffer sizes of 568 entries (in a full K computer simulation), such that for this model the assumed buffer size of 1000 is a worst-case scenario.</p>
<p>Neuron objects in NEST are distributed across virtual processes in a round-robin fashion and connections are represented on the process of their post-synaptic neuron. We use the term &#x0201C;VP-local&#x0201D; to indicate that a neuron is local to a certain virtual process. As neurons with similar properties are typically created en bloc, the round-robin distribution scheme constitutes a simple form of static load-balancing for heterogeneous networks with varying numbers of incoming connections per neuron. If each virtual process owns sufficiently many neurons, the number of local connection objects is similar across processes. Therefore, in our model we let <italic>N</italic><sub><italic>M</italic></sub> &#x0003D; <italic>N</italic>/<italic>M</italic> and <italic>K</italic><sub><italic>M</italic></sub> &#x0003D; <italic>N</italic><sub><italic>M</italic></sub><italic>K</italic> denote the average number of neuron and connection objects per MPI process, and we let <italic>N</italic><sub>VP</sub> &#x0003D; <italic>N</italic><sub><italic>M</italic></sub>/<italic>T</italic> and <italic>K</italic><sub>VP</sub> &#x0003D; <italic>N</italic><sub>VP</sub><italic>K</italic> denote the average number of VP-local neuron and connection objects.</p>
<p>In the regime of &#x0007E; 10,000 virtual processes, for a randomly connected network the targets of a neuron become more and more spread out. This results in the limiting case where <italic>K</italic> processes each own one of the targets and the remaining <italic>MT</italic> &#x02212; <italic>K</italic> processes do not own any of the targets. As a consequence, the connection infrastructure becomes increasingly sparse, where the extent of sparseness can be quantified in a probabilistic way. Here we use the symbol &#x0007E; reading &#x0201C;on the order of&#x0201D; (Hardy and Wright, <xref ref-type="bibr" rid="B20">1975</xref>, p. 7) in the physics sense (Jeffreys and Jeffreys, <xref ref-type="bibr" rid="B25">1956</xref>, p. 23). This relation stating that two quantities are not differing by more than a factor of 10 needs to be distinguished from the big-O notation below (Section 2.4), which is used to describe the limit of a function.</p>
<p>To quantify the sparseness we define <italic>p</italic><sub>&#x02205;</sub> and <italic>p</italic><sub>1</sub> as the probabilities that a particular neuron has 0 or 1 local target on a given virtual process, respectively. Each neuron draws on average <italic>K</italic> source neurons from the set of <italic>N</italic> possible source neurons. If the incoming connections per neuron are drawn independently, on average <italic>K</italic><sub>VP</sub> source neurons are drawn on each virtual process. Due to the large numbers, the distribution around this mean value is narrow. The probability that a particular neuron is drawn as a source is 1/<italic>N</italic> and the probability that the neuron is not drawn as a source is 1 &#x02212; 1/<italic>N</italic>. We can therefore adopt the simplifying assumption that <italic>p</italic><sub>&#x02205;</sub> &#x0003D; (1 &#x02212; 1/<italic>N</italic>)<sup><italic>K</italic><sub>VP</sub></sup> expresses the average probability that a neuron does not connect to any VP-local target, such that <italic>N</italic><sup>&#x02205;</sup><sub>c</sub> &#x0003D; <italic>p</italic><sub>&#x02205;</sub><italic>N</italic> denotes the expected number of neurons without any VP-local target. In this study we adapt the model to separately account for the neurons with exactly one VP-local target and for those with more than one VP-local target. We introduce <italic>p</italic><sub>1</sub> &#x0003D; (1 &#x02212; 1/<italic>N</italic>)<sup><italic>K</italic><sub>VP</sub>&#x02212;1</sup> <italic>K</italic><sub>VP</sub>/<italic>N</italic> as the average probability that a neuron has exactly one local target, such that <italic>N</italic><sup>1</sup><sub>c</sub> &#x0003D; <italic>p</italic><sub>1</sub><italic>N</italic> denotes the expected number of neurons with only one local target. The remaining <italic>N</italic> &#x02212; <italic>N</italic><sup>&#x02205;</sup><sub>c</sub> &#x02212; <italic>N</italic><sup>1</sup><sub>c</sub> neurons connect to more than one VP-local target.</p>
<p>Throughout this study, we keep the average number of incoming connections per neuron fixed at either <italic>K</italic> &#x0003D; 11,250 or <italic>K</italic> &#x0003D; 6000 in accordance with the two employed benchmark network models (see Section 2.2), and we assume <italic>T</italic> &#x0003D; 8 threads per MPI process, which corresponds to the maximum number of threads per node supported on the K computer (see Section 2.5). We explicitly differentiate between connections with spike-timing dependent plasticity and connections with static weights. This is a trivial but useful extension to the model, which enables a more precise prediction of memory usage. For the case that all excitatory-excitatory connections exhibit STDP, the number of STDP connections per MPI process amounts to <italic>K</italic><sup>stdp</sup><sub><italic>M</italic></sub> &#x0003D; <italic>K</italic><sub><italic>M</italic></sub>&#x003B2;<sup>2</sup>, where &#x003B2; &#x0003D; 0.8 is the fraction of excitatory neurons, and the remaining <italic>K</italic><sup>stat</sup><sub><italic>M</italic></sub> &#x0003D; <italic>K</italic><sub><italic>M</italic></sub> &#x02212; <italic>K</italic><sup>stdp</sup><sub><italic>M</italic></sub> synapses are static. In Helias et al. (<xref ref-type="bibr" rid="B21">2012</xref>), this differentiation between two connection types was not required as in NEST 2.2 (3g kernel) the employed models <monospace>stdp_pl_synapse_hom</monospace> and <monospace>static_synapse</monospace> have an identical memory usage of <italic>m</italic><sup>stdp</sup><sub>c</sub> &#x0003D; <italic>m</italic><sup>stat</sup><sub>c</sub> &#x0003D; 48 B.</p>
<p>With the above definitions, the memory consumption of the latter two terms of Equation (1) can be further decomposed into</p>
<graphic xlink:href="fninf-08-00078-i0003.tif"/>
<graphic xlink:href="fninf-08-00078-i0004.tif"/>
<p>in order to capture the contributions of neuron and synapse objects and different parts of infrastructure. Table <xref ref-type="table" rid="T1">1</xref> summarizes the model parameters required to specify <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>n</sub> (<italic>M</italic>, <italic>N</italic>) and <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>c</sub> (<italic>M</italic>, <italic>T</italic>, <italic>N</italic>, <italic>K</italic>) and contrasts their values for the 3g (Helias et al., <xref ref-type="bibr" rid="B21">2012</xref>) and 4g simulation technology. For convenience, we provide the values already at this point even though they are explained only in Section 3.</p>
<table-wrap position="float" id="T1">
<label>Table 1</label>
<caption><p><bold>Parameter definitions and values of memory-usage model for 3g and 4g technology</bold>.</p></caption>
<table frame="hsides" rules="groups">
<thead>
<tr>
<th/>
<th align="center"><bold>Parameter</bold></th>
<th align="left"><bold>Description</bold></th>
<th align="center" colspan="2"><bold>Value in B</bold></th>
</tr>
<tr>
<th/>
<th/>
<th/>
<th align="center"><bold>3g</bold></th>
<th align="center"><bold>4g</bold></th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>n</sub> (<italic>M</italic>, <italic>N</italic>)</td>
<td align="center"><italic>m</italic><sub>n</sub></td>
<td align="left">memory usage of one neuron object of type <monospace>iaf_psc_alpha</monospace></td>
<td align="center" colspan="2">1100</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>0</sup><sub>n</sub></td>
<td align="left">memory overhead per neuron</td>
<td align="center">0.33</td>
<td align="center">0</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>&#x0002B;</sup><sub>n</sub></td>
<td align="left">memory overhead per local neuron</td>
<td align="center">16</td>
<td align="center">24</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>&#x02205;</sup><sub>n</sub></td>
<td align="left">memory overhead per non-local neuron</td>
<td align="center" colspan="2">0</td>
</tr>
<tr>
<td align="left"><inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>c</sub> (<italic>M</italic>, <italic>T</italic>, <italic>N</italic>, <italic>K</italic>)</td>
<td align="center"><italic>m</italic><sup>stat</sup><sub>c</sub></td>
<td align="left">memory usage of one connection object of type <monospace>static_synapse</monospace> (3g) or <monospace>static_synapse_hpc</monospace> (4g)</td>
<td align="center">48</td>
<td align="center">16</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>stdp</sup><sub>c</sub></td>
<td align="left">memory usage of one connection object of type <monospace>stdp_pl_synapse</monospace> (3g) or <monospace>stdp_pl_synapse_hpc</monospace> (4g)</td>
<td align="center">48</td>
<td align="center">24</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>0</sup><sub>c</sub></td>
<td align="left">memory overhead per neuron</td>
<td align="center" colspan="2">0.33</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>1</sup><sub>c</sub></td>
<td align="left">memory overhead per neuron with one local target</td>
<td align="center">96</td>
<td align="center">24</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>&#x0003E;1</sup><sub>c</sub></td>
<td align="left">memory overhead per neuron with more than one local target</td>
<td align="center">160</td>
<td align="center">128</td>
</tr>
<tr>
<td/>
<td align="center"><italic>m</italic><sup>&#x02205;</sup><sub>c</sub></td>
<td align="left">memory overhead per neuron without local targets</td>
<td align="center" colspan="2">0</td>
</tr>
</tbody>
</table>
<table-wrap-foot>
<p><italic>The top part of the table summarizes the parameters relevant for the memory consumption due to the neuronal infrastructure <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>#1</sub>, the bottom part shows the parameters determining the memory consumption <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>c</sub> of the synaptic infrastructure. Lower case symbols <italic>m</italic> refer to the memory per object, where objects are neurons or connections, respectively. The columns &#x0201C;3g&#x0201D; and &#x0201C;4g&#x0201D; distinguish between the parameter values for the two kernel versions.</italic></p>
</table-wrap-foot>
</table-wrap>
<p>Note that we assume the same overhead <italic>m</italic><sup>&#x0003E;1</sup><sub><italic>c</italic></sub> for all neurons with more than one local target, which means that we do not introduce any further distinction of possible synapse containers for the cases where more than one synapse needs to be stored (see Section 3.2 for the details). Here, we set <italic>m</italic><sup>&#x0003E;1</sup><sub><italic>c</italic></sub> such that it corresponds to the most complex synapse container that can occur in simulations of the benchmark network model described in Section 2.2, which is a container that stores two different types of synapses in corresponding <monospace>vectors</monospace>. As a result of this worst-case assumption the model produces a slight overestimation of memory consumption.</p>
<p>Overall, however, the model underestimates the effectively required memory resources as the theoretically determined parameter values that we employ here reflect only the memory usage of the involved data types on a 64 bit architecture, but they cannot account for the memory allocation strategies of the involved dynamical data structures (Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>).</p>
</sec>
<sec>
<title>2.4. Number and length of local target lists</title>
<p>Using the notation of Section 2.3 the probability of a neuron to be the source of a particular synapse is 1/<italic>N</italic> and consequently the probability of not being the source of any of the <italic>K</italic><sub>VP</sub> &#x0003D; <italic>KN</italic>/(<italic>MT</italic>) VP-local synapses is</p>
<disp-formula id="E1"><label>(4)</label><mml:math id="M3"><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mo>&#x02205;</mml:mo></mml:msub><mml:mo>=</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:mfrac><mml:mrow><mml:mi>K</mml:mi><mml:mi>N</mml:mi></mml:mrow><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Empty target lists are not instantiated and therefore do not cause overhead by themselves. We recognize in Equation (4) the structure (1 &#x0002B; <italic>x</italic>/<italic>N</italic>)<sup><italic>N</italic></sup> exposed by</p>
<disp-formula id="E2"><mml:math id="M4"><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mo>&#x02205;</mml:mo></mml:msub><mml:mo>=</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>[</mml:mo><mml:mrow><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mi>N</mml:mi></mml:msup></mml:mrow><mml:mo>]</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Thus, in the limit of large <italic>N</italic> we can use the definition of the exponential function lim<sub><italic>N</italic>&#x02192;&#x0221E;</sub> (1 &#x0002B; <italic>x</italic>/<italic>N</italic>)<sup><italic>N</italic></sup> &#x0003D; exp(<italic>x</italic>) to replace the term [&#x000B7;]. Conceptually this corresponds to the approximation of the binomial probabilities <inline-formula><mml:math id="M5"><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mtable><mml:mtr><mml:mtd><mml:mi>N</mml:mi></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mi>k</mml:mi></mml:mtd></mml:mtr></mml:mtable></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:msup><mml:mi>p</mml:mi><mml:mi>k</mml:mi></mml:msup><mml:msup><mml:mrow><mml:mo stretchy='false'>(</mml:mo><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mi>p</mml:mi><mml:mo stretchy='false'>)</mml:mo></mml:mrow><mml:mrow><mml:mi>N</mml:mi><mml:mo>&#x02212;</mml:mo><mml:mi>k</mml:mi></mml:mrow></mml:msup></mml:mrow></mml:math></inline-formula> by the corresponding Poisson probabilities <inline-formula><mml:math id="M6"><mml:mrow><mml:mfrac><mml:mrow><mml:msup><mml:mi>&#x003BB;</mml:mi><mml:mi>k</mml:mi></mml:msup></mml:mrow><mml:mrow><mml:mi>k</mml:mi><mml:mo>!</mml:mo></mml:mrow></mml:mfrac><mml:mi>exp</mml:mi><mml:mo stretchy='false'>(</mml:mo><mml:mo>&#x02212;</mml:mo><mml:mi>&#x003BB;</mml:mi><mml:mo stretchy='false'>)</mml:mo></mml:mrow></mml:math></inline-formula>, where &#x003BB; &#x0003D; <italic>Np</italic> &#x0003D; const. as <italic>N</italic> &#x02192; &#x0221E;. In this limit we have</p>
<disp-formula id="E3"><label>(5)</label><mml:math id="M7"><mml:mtable columnalign='left'><mml:mtr><mml:mtd><mml:msub><mml:mover accent='true'><mml:mi>p</mml:mi><mml:mo>&#x002DC;</mml:mo></mml:mover><mml:mo>&#x02205;</mml:mo></mml:msub><mml:mo>=</mml:mo><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>&#x02243;</mml:mo><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac><mml:mo>+</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mn>2</mml:mn></mml:msup><mml:mo>+</mml:mo><mml:mi>O</mml:mi><mml:mrow><mml:mo>[</mml:mo><mml:mrow><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>3</mml:mn></mml:msup></mml:mrow><mml:mo>]</mml:mo></mml:mrow><mml:mo>,</mml:mo></mml:mtd></mml:mtr></mml:mtable></mml:math></disp-formula>
<p>where in the second line we expanded the expression up to second order in the ratio <inline-formula><mml:math id="M8"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x0226A; 1. We here use the big-O notation in the sense of infinitesimal asymptotics, which means that <inline-formula><mml:math id="M9"><mml:mrow><mml:mi>O</mml:mi><mml:mrow><mml:mo>[</mml:mo><mml:mrow><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>3</mml:mn></mml:msup></mml:mrow><mml:mo>]</mml:mo></mml:mrow></mml:mrow></mml:math></inline-formula> collects all terms of the form <inline-formula><mml:math id="M10"><mml:mrow><mml:mi>f</mml:mi><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow></mml:math></inline-formula> such that for any small <inline-formula><mml:math id="M11"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> there exists a constant <italic>C</italic> fulfilling the relation <inline-formula><mml:math id="M12"><mml:mrow><mml:mo>&#x0007C;</mml:mo><mml:mi>f</mml:mi><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mo>&#x0007C;</mml:mo><mml:mtext>&#x02009;</mml:mtext><mml:mo>&#x0003C;</mml:mo><mml:mi>C</mml:mi><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>3</mml:mn></mml:msup></mml:mrow></mml:math></inline-formula> as <inline-formula><mml:math id="M13"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x02192; 0. In complexity theory big-O often implicitly denotes the infinite asymptotics when it refers to an integer variable <italic>n</italic>. Both use cases of the notation are intended and comply with its definition (Knuth, <xref ref-type="bibr" rid="B26">1997</xref>, section 1.2.11.1).</p>
<p>The expected number of target lists with at least one synapse is</p>
<disp-formula id="E4"><label>(6)</label><mml:math id="M14"><mml:mtable columnalign='left'><mml:mtr><mml:mtd><mml:msubsup><mml:mi>N</mml:mi><mml:mtext>c</mml:mtext><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>0</mml:mn></mml:mrow></mml:msubsup><mml:mo>=</mml:mo><mml:mo stretchy='false'>(</mml:mo><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msub><mml:mi>p</mml:mi><mml:mo>&#x02205;</mml:mo></mml:msub><mml:mo stretchy='false'>)</mml:mo><mml:mi>N</mml:mi></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>=</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:mfrac><mml:mrow><mml:mi>K</mml:mi><mml:mi>N</mml:mi></mml:mrow><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mi>N</mml:mi><mml:mo>.</mml:mo></mml:mtd></mml:mtr></mml:mtable></mml:math></disp-formula>
<p>For the weak scaling shown in Figure <xref ref-type="fig" rid="F1">1</xref> we express <italic>N</italic> &#x0003D; <italic>N</italic><sub>VP</sub><italic>MT</italic> in terms of the number of local neurons per virtual process <italic>N</italic><sub>VP</sub>. Using the definition of <italic>K</italic><sub>VP</sub>, Equation (6) becomes</p>
<disp-formula id="E5"><label>(7)</label><mml:math id="M15"><mml:mrow><mml:msubsup><mml:mover accent='true'><mml:mi>N</mml:mi><mml:mo>&#x002DC;</mml:mo></mml:mover><mml:mi>c</mml:mi><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>0</mml:mn></mml:mrow></mml:msubsup><mml:mo>=</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mi>N</mml:mi></mml:mfrac></mml:mrow></mml:msup></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mi>N</mml:mi><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>In weak scaling the total number of local synapses <italic>K</italic><sub>VP</sub> remains constant and we find the limit of <italic>N</italic><sup>&#x0003E;0</sup><sub>c</sub> by approximating the exponential to linear order</p>
<disp-formula id="E6"><mml:math id="M16"><mml:mrow><mml:munder><mml:mrow><mml:mi>lim</mml:mi></mml:mrow><mml:mrow><mml:mi>N</mml:mi><mml:mo>&#x02192;</mml:mo><mml:mi>&#x0221E;</mml:mi></mml:mrow></mml:munder><mml:msubsup><mml:mi>N</mml:mi><mml:mtext>c</mml:mtext><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>0</mml:mn></mml:mrow></mml:msubsup><mml:mo>=</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mi>N</mml:mi><mml:mo>=</mml:mo><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Using Equation (7) the number of neurons <italic>N</italic><sub>&#x003B6;</sub> at which a fraction &#x003B6; of the maximal number of target lists <italic>K</italic><sub>VP</sub> contains at least one synapse is given by the relation</p>
<disp-formula id="E7"><label>(8)</label><mml:math id="M17"><mml:mrow><mml:mi>&#x003B6;</mml:mi><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mo>=</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi></mml:msub></mml:mrow></mml:mfrac></mml:mrow></mml:msup></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi></mml:msub><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>With the substitution <italic>s</italic> &#x0003D; &#x02212;<italic>K</italic><sub>VP</sub>/<italic>N</italic><sub>&#x003B6;</sub> the relation is of the form <italic>e<sup>s</sup></italic> &#x0003D; 1 &#x0002B; &#x003B6; <italic>s</italic> and can be inverted using the Lambert-<italic>W</italic> function (Corless et al., <xref ref-type="bibr" rid="B8">1996</xref>) yielding</p>
<disp-formula id="E8"><label>(9)</label><mml:math id="M18"><mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B1;</mml:mi></mml:msub><mml:mo>=</mml:mo><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mi>&#x003B6;</mml:mi><mml:msup><mml:mrow><mml:mrow><mml:mo>[</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>+</mml:mo><mml:mi>&#x003B6;</mml:mi><mml:mi>W</mml:mi><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mrow><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>&#x003B6;</mml:mi></mml:mfrac></mml:mrow></mml:msup></mml:mrow><mml:mi>&#x003B6;</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mo>]</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Starting again from Equation (8) with a second order approximation for the exponential</p>
<disp-formula id="E9"><mml:math id="M19"><mml:mrow><mml:mi>&#x003B6;</mml:mi><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mo>&#x02243;</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi></mml:msub></mml:mrow></mml:mfrac><mml:mo>+</mml:mo><mml:mfrac><mml:mrow><mml:msubsup><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow><mml:mn>2</mml:mn></mml:msubsup></mml:mrow><mml:mrow><mml:mn>2</mml:mn><mml:msubsup><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi><mml:mn>2</mml:mn></mml:msubsup></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi></mml:msub></mml:mrow></mml:math></disp-formula>
<p>the relation depends linearly on <italic>N</italic><sub>&#x003B6;</sub>, so</p>
<disp-formula id="E10"><label>(10)</label><mml:math id="M20"><mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi></mml:msub><mml:mo>&#x02243;</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mrow><mml:mn>2</mml:mn><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mi>&#x003B6;</mml:mi></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow></mml:mfrac><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Following Equation (4) the probability of a particular neuron to establish exactly one synapse with a local neuron is</p>
<disp-formula id="E11"><label>(11)</label><mml:math id="M21"><mml:mtable columnalign='left'><mml:mtr><mml:mtd><mml:msub><mml:mi>p</mml:mi><mml:mn>1</mml:mn></mml:msub><mml:mo>=</mml:mo><mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mrow><mml:mi>K</mml:mi><mml:mi>N</mml:mi><mml:mo>/</mml:mo><mml:mi>M</mml:mi><mml:mi>T</mml:mi><mml:mo>&#x02212;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mfrac><mml:mrow><mml:mi>K</mml:mi><mml:mi>N</mml:mi></mml:mrow><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>&#x02243;</mml:mo><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>&#x02243;</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac><mml:mo>+</mml:mo><mml:mi>O</mml:mi><mml:mrow><mml:mo>[</mml:mo><mml:mrow><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>3</mml:mn></mml:msup></mml:mrow><mml:mo>]</mml:mo></mml:mrow><mml:mo>.</mml:mo></mml:mtd></mml:mtr></mml:mtable></mml:math></disp-formula>
<p>Therefore the expected number of target lists with exactly one synapse is</p>
<disp-formula id="E12"><label>(12)</label><mml:math id="M22"><mml:mtable columnalign='left'><mml:mtr><mml:mtd><mml:msubsup><mml:mi>N</mml:mi><mml:mtext>c</mml:mtext><mml:mn>1</mml:mn></mml:msubsup><mml:mo>=</mml:mo><mml:msub><mml:mi>p</mml:mi><mml:mn>1</mml:mn></mml:msub><mml:mi>N</mml:mi><mml:mo>=</mml:mo><mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mo>&#x02212;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>=</mml:mo><mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi><mml:msub><mml:mi>N</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac><mml:mo>&#x02212;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac><mml:mo>.</mml:mo></mml:mtd></mml:mtr></mml:mtable></mml:math></disp-formula>
<p>Naturally <italic>N</italic><sup>1</sup><sub>c</sub> has the same limit <italic>K</italic><sub>VP</sub> as <italic>N</italic><sup>&#x0003E;0</sup><sub>c</sub>. The probability to establish more than one synapse with a local neuron is the remainder</p>
<disp-formula id="E13"><label>(13)</label><mml:math id="M23"><mml:mtable columnalign='left'><mml:mtr><mml:mtd><mml:msub><mml:mi>p</mml:mi><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msub><mml:mo>=</mml:mo><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msub><mml:mi>p</mml:mi><mml:mo>&#x02205;</mml:mo></mml:msub><mml:mo>&#x02212;</mml:mo><mml:msub><mml:mi>p</mml:mi><mml:mn>1</mml:mn></mml:msub></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>=</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow></mml:msup><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mo>&#x02212;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>&#x02243;</mml:mo><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mi>e</mml:mi><mml:mrow><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:msup><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>&#x02243;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mn>2</mml:mn></mml:msup><mml:mo>&#x02212;</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac><mml:mo>+</mml:mo><mml:mi>O</mml:mi><mml:mrow><mml:mo>[</mml:mo><mml:mrow><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>3</mml:mn></mml:msup></mml:mrow><mml:mo>]</mml:mo></mml:mrow></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>=</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mn>2</mml:mn></mml:msup><mml:mo>+</mml:mo><mml:mi>O</mml:mi><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>3</mml:mn></mml:msup></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mo>,</mml:mo></mml:mtd></mml:mtr></mml:mtable></mml:math></disp-formula>
<p>where from the second to the third line we ignored the &#x02212;1 in the exponent, identified the exponential function in the limit, and from the third to the fourth line approximated the expression consistently up to second order in <inline-formula><mml:math id="M24"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula>. The expected number of such target lists is</p>
<disp-formula id="E14"><mml:math id="M25"><mml:mtable columnalign='left'><mml:mtr><mml:mtd><mml:msup><mml:mi>N</mml:mi><mml:mrow><mml:mtext>&#x0200A;</mml:mtext><mml:mo>&#x0003E;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:mo>=</mml:mo><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow></mml:msup><mml:mo>&#x02212;</mml:mo><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub><mml:mo>&#x02212;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msup><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mo>)</mml:mo></mml:mrow><mml:mi>N</mml:mi></mml:mtd></mml:mtr><mml:mtr><mml:mtd><mml:mtext>&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;&#x02009;</mml:mtext><mml:mo>&#x02243;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:msubsup><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow><mml:mn>2</mml:mn></mml:msubsup><mml:mfrac><mml:mn>1</mml:mn><mml:mi>N</mml:mi></mml:mfrac><mml:mo>,</mml:mo></mml:mtd></mml:mtr></mml:mtable></mml:math></disp-formula>
<p>which can be expressed in terms of <italic>MT</italic> and <italic>N</italic><sub>V<italic>P</italic></sub> by noting that <italic>N</italic> &#x0003D; <italic>N</italic><sub>VP</sub><italic>MT</italic> and <italic>K</italic><sub>VP</sub> &#x0003D; <inline-formula><mml:math id="M26"><mml:mrow><mml:mfrac><mml:mrow><mml:mi>K</mml:mi><mml:mi>N</mml:mi></mml:mrow><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x0003D; <italic>N</italic><sub>VP</sub><italic>K</italic>. The limit exposes that the number of target lists with more than one synapse declines hyperbolically with <italic>N</italic>.</p>
</sec>
<sec>
<title>2.5. Supercomputers</title>
<p>The compute nodes in contemporary supercomputers contain multi-core processors; the trend toward ever greater numbers of cores is further manifested in the BlueGene/Q architecture with 16 cores per node, each capable of running 4 hardware threads. These architectures feature a multi-level parallel programming model, each level potentially operating at different granularity. The coarsest level is provided by the process based distribution, using MPI for inter-process communication message passing interface (Message Passing Interface Forum, <xref ref-type="bibr" rid="B31">1994</xref>). Within each process, the next finer level is covered by threads, which can be forked and joined in a flexible manner with OpenMP enabled compilers (Board, <xref ref-type="bibr" rid="B6">2008</xref>). The finest level is provided by streaming instructions that make use of concurrently operating floating point units within each core.</p>
<p>To evaluate the scalability of NEST in terms of run time and memory usage we performed benchmarks on two different distributed-memory supercomputing systems: the JUQUEEN BlueGene/Q at the J&#x000FC;lich Research Centre in Germany and the K computer at the Advanced Institute for Computational Science in Kobe, Japan. The K computer consists of 88,128 compute nodes, each with an 8-core SPARC64 VIIIfx processor, which operates at a clock frequency of 2 GHz (Yonezawa et al., <xref ref-type="bibr" rid="B46">2011</xref>), whereas the JUQUEEN supercomputer comprises 28,672 nodes, each with a 16-core IBM PowerPC A2 processor, which runs at 1.6 GHz. Both systems support a hybrid simulation scheme: distributed-memory parallel computing with MPI and multithreading on the processor level. In addition, the individual cores of a JUQUEEN processor support simultaneous multithreading with up to 4 threads. Both supercomputers have 16 GB of random access memory (RAM) available per compute node such that in terms of total memory resources the K computer is more than three times larger than JUQUEEN. The compute nodes of the K computer are connected with the &#x0201C;Tofu&#x0201D; (<italic>to</italic>rus connected <italic>fu</italic>ll connection) interconnect network, which is a six-dimensional mesh/torus network (Ajima et al., <xref ref-type="bibr" rid="B1">2009</xref>). The bandwidth per link is 5 GB/s. JUQUEEN uses a five-dimensional torus interconnect network with a bandwidth of 2 GB/s per link.</p>
<p>In this study all benchmarks were run with <italic>T</italic> &#x0003D; 8 OpenMP threads per compute node, which on both systems results in 2 GB of memory per thread, and hence facilitates the direct comparison of benchmarking results between the two systems. With this setup we exploited all cores per node on the K computer but only half of the cores per node on JUQUEEN. In particular we did not make use of the hardware support for multithreading the individual processor cores of JUQUEEN already provide. In total on JUQUEEN only 8 of the 64 hardware supported threads were used.</p>
</sec>
<sec>
<title>2.6. Maximum-filling scaling</title>
<p>To obtain the maximum-filling scalings shown in Figures <bold>7&#x02013;11</bold> we followed a two step procedure. First, based on the memory-usage model, we obtain a prediction of the maximum number of neurons fitting on a given portion of the machine. We then run a series of &#x0201C;dry runs,&#x0201D; where the number of neurons is varied around the predicted value. The dry run is a feature of NEST that we originally developed to validate our model of the simulator&#x00027;s memory usage (Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>). A dry run executes the same script as the actual simulation, but only uses one compute node. This feature can be enabled in NEST at run time by the simulation script. Due to the absence of the <italic>M</italic> &#x02212; 1 other instances, the script can only be executed up to the point where the first communication takes place, namely until after the connectivity has been set up. At this point, however, the bulk of the memory has been allocated so that a good estimate of the resources can be obtained and the majority of the simulation script has been executed. In order to establish the same data structures as in the full run, the kernel needs to be given the information about the total number of processes in the actual simulation. This procedure also takes into account that of the nominal amount of working memory (e.g., 16 GB per processor on K) typically only a fraction (13.81 GB per processor on K) is actually available for the user program.</p>
</sec>
</sec>
<sec sec-type="results" id="s3">
<title>3. Results</title>
<sec>
<title>3.1. Memory usage in the petascale regime</title>
<p>The kernel of NEST 2.2 (3g kernel) is discussed in detail in Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>) and Helias et al. (<xref ref-type="bibr" rid="B21">2012</xref>). In Figure <xref ref-type="fig" rid="F2">2</xref> we compare the memory consumptions of the 3g kernel and the 4g kernel depending on the number of employed cores <italic>MT</italic>. We choose the number of neurons such that at each machine size the 4g kernel consumes the entire available memory. For the same network size, we estimate the memory consumption that the 3g kernel would require. The upper panel of Figure <xref ref-type="fig" rid="F2">2</xref> shows the different contributions to memory consumption for this earlier kernel. In the following we identify the dominant contributions in the limit of large machines used to guide the development of the 4g kernel. The resulting implementation of the 4g kernel is described in Sections 3.2 to 3.5.</p>
<fig id="F2" position="float">
<label>Figure 2</label>
<caption><p><bold>Predicted cumulative memory usage as a function of number of virtual processes for a maximum-filling scaling</bold>. Contributions of different data structure components to total memory usage <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/> (<italic>M</italic>, <italic>T</italic>, <italic>N</italic>, <italic>K</italic>) of NEST for a network that just fits on <italic>MT</italic> cores of the K computer when using the 4g kernel with <italic>T</italic> &#x0003D; 8. Contributions of synapse objects and relevant components of connection infrastructure are shown in pink and shades of orange, respectively. Contributions of base memory usage, neuron objects, and neuron infrastructure are significantly smaller and hence not visible at this scale. <italic>K</italic> &#x0003D; 11,250 synapses per neuron are assumed. Dark orange: sparse table, orange: intermediate infrastructure containing exactly 1 synapse, light orange: intermediate infrastructure containing more than 1 synapse. Predicted memory usage is shown for 3g (upper panel) and 4g technology (lower panel) with identical scaling of the vertical axes. Vertical dashed black lines indicate full size of the K computer; horizontal dashed black lines indicate maximum memory usage measured on K.</p></caption>
<graphic xlink:href="fninf-08-00078-g0002.tif"/>
</fig>
<p>In simulations running <italic>MT</italic> &#x0007E; 100 (we use &#x0007E; to read &#x0201C;on the order of&#x0201D;) virtual processes, synapse objects take up most of the available memory. Hence, on small clusters a good approximation of the maximum possible network size is given by <italic>N</italic><sub>max</sub> &#x02248; <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>max</sub>/(<italic>Km</italic><sub>c</sub>) where <inline-graphic xlink:href="fninf-08-00078-i0001.tif"/><sub>max</sub> denotes the amount of memory available per MPI process. In the range of <italic>MT</italic> &#x0007E; 1000 virtual processes we observe a transition where due to an insufficient parallelization of data structures the connection infrastructure (shades of orange) starts to dominate the total memory consumption. For sufficiently small machine sizes <italic>MT</italic> &#x0003C;&#x0007E; 1000 the intermediate synaptic infrastructure (shown in orange in Figure <xref ref-type="fig" rid="F3">3A</xref>) typically stores on each virtual process more than one outgoing synapse for each source neuron. The entailed memory overhead is therefore negligible compared with the memory consumed by the actual synapse objects. As <italic>MT</italic> increases, the target lists become progressively shorter; the proportion of source neurons for which the target lists only store a single connection increases. We obtain a quantitative measure by help of the memory model presented in Section 3.1, considering the limit of very large machines where <italic>K</italic>/(<italic>MT</italic>) &#x0226A; 1. In this limit we can consistently expand all quantities up to second order in the ratio <inline-formula><mml:math id="M27"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x0226A; 1. The probability (5) for a source neuron on a given machine to have an empty target list approaches unity <italic>p</italic><sub>0</sub> &#x02192; 1. Correspondingly, the probability for a target list with exactly one entry (11) approaches <italic>p</italic><sub>1</sub> &#x02192; <inline-formula><mml:math id="M28"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula>. Target lists with more than one entry become quadratically unlikely in the small parameter <inline-formula><mml:math id="M29"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> (13),</p>
<disp-formula id="E15"><mml:math id="M30"><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mrow><mml:mtext>&#x0200A;</mml:mtext><mml:mo>&#x0003E;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msub><mml:mo>&#x02243;</mml:mo><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:msup><mml:mrow><mml:mrow><mml:mo>(</mml:mo><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow><mml:mo>)</mml:mo></mml:mrow></mml:mrow><mml:mn>2</mml:mn></mml:msup><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<fig id="F3" position="float">
<label>Figure 3</label>
<caption><p><bold>VP-local connection infrastructure of NEST</bold>. A sparse table (dark orange structure and attached light orange boxes with arrows) holds the thread-local connection objects (pink squares) sorted according to the global index of the associated presynaptic node. The sparse table consists of <italic>n</italic><sub>gr</sub> equally-sized groups, where each group maintains a bit field (tiny squares) with one bit for each global node index in the group indicating the presence or absence of local targets. If a particular node has local targets, the sparse table stores a pointer to an additional inner data structure (light orange), which has undergone a major redesign during the software development process that led from the 3g to the 4g kernel. <bold>(A)</bold> Connection infrastructure of the 3g kernel; listed Byte counts contribute to <italic>m</italic><sup>&#x0002B;</sup><sub>c</sub> [see Equation (3)]. The inner data structure consists of a <monospace>vector</monospace>, which holds a <monospace>struct</monospace> for each connection type that the node has locally in use. Each <monospace>struct</monospace> links the id of a particular connection type with a pointer to the <monospace>Connector</monospace> that stores the connection objects of this type in a <monospace>vector</monospace>. <bold>(B)</bold> Auto-adjusting connection infrastructure of the 4g kernel. Case 1: A particular node has less than <italic>K</italic><sub>cutoff</sub> local connections and all are of the same type. A lightweight inner structure (<monospace>HomConnector</monospace>) stores the connection objects in a fixed-size array. Listed Byte counts contribute to <italic>m</italic><sup>1</sup><sub>c</sub>. Case 2: A particular node has at least <italic>K</italic><sub>cutoff</sub> local connections and all are of the same type. A <monospace>HomConnector</monospace> stores the connection objects in a dynamically-sized <monospace>vector</monospace>. Case 3: The local connections of a particular node are of different types. A <monospace>HetConnector</monospace>, which is derived from C&#x0002B;&#x0002B; <monospace>vector</monospace>, holds a <monospace>HomConnector</monospace> (either Case 1 or 2) for each connection type that the node has locally in use.</p></caption>
<graphic xlink:href="fninf-08-00078-g0003.tif"/>
</fig>
<p>Two observations can be made from these expressions. In the sparse limit, the probabilities become independent of the number of neurons. For small <inline-formula><mml:math id="M31"><mml:mrow><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula>, target lists are short and, if not empty, typically contain only a single connection. To illustrate these estimates on a concrete example we assume the simulation of a network with <italic>K</italic> &#x0003D; 10<sup>4</sup> synapses per neuron distributed across the processors of a supercomputer, such as the K computer, with <italic>M</italic> &#x02243; 80,000 CPUs and <italic>T</italic> &#x0003D; 8 threads each. The above estimates then yield <italic>p</italic><sub>&#x02205;</sub> &#x02243; 0.984, <italic>p</italic><sub>1</sub> &#x02243; 0.015, and <italic>p</italic><sub>&#x0003E;1</sub> &#x02243; 0.00012. Hence, given there is at least one connection, the conditional probability to have one synapse is <inline-formula><mml:math id="M32"><mml:mrow><mml:mfrac><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mn>1</mml:mn></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mn>1</mml:mn></mml:msub><mml:mo>+</mml:mo><mml:msub><mml:mi>p</mml:mi><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msub></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x02243; 1 &#x02212; <inline-formula><mml:math id="M33"><mml:mrow><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x02243; 0.992 and the conditional probability to have more than one synapse is only <inline-formula><mml:math id="M34"><mml:mrow><mml:mfrac><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msub></mml:mrow><mml:mrow><mml:msub><mml:mi>p</mml:mi><mml:mn>1</mml:mn></mml:msub><mml:mo>+</mml:mo><mml:msub><mml:mi>p</mml:mi><mml:mrow><mml:mo>&#x0003E;</mml:mo><mml:mn>1</mml:mn></mml:mrow></mml:msub></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x02243; <inline-formula><mml:math id="M35"><mml:mrow><mml:mfrac><mml:mn>1</mml:mn><mml:mn>2</mml:mn></mml:mfrac><mml:mfrac><mml:mi>K</mml:mi><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula> &#x02243; 0.008.</p>
<p>Figure <xref ref-type="fig" rid="F1">1</xref> shows the number of non-empty target lists under weak scaling as a function of the network size <italic>N</italic>. In the limit of large networks this number approaches <inline-formula><mml:math id="M36"><mml:mrow><mml:mfrac><mml:mrow><mml:mi>N</mml:mi><mml:mi>K</mml:mi></mml:mrow><mml:mrow><mml:mi>M</mml:mi><mml:mi>T</mml:mi></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula>, and is thus equal to the number of local synapses terminating on the respective machine. The size of the network <italic>N</italic><sub>&#x003B6;</sub> at which the number of target lists has reached a fraction &#x003B6; &#x02243; 1 of the maximum number of lists is given by <italic>N</italic><sub>&#x003B6;</sub> (10) with <italic>N</italic><sub>&#x003B6;</sub> &#x02243; <inline-formula><mml:math id="M37"><mml:mrow><mml:msub><mml:mi>N</mml:mi><mml:mi>&#x003B6;</mml:mi></mml:msub><mml:mo>&#x02243;</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>K</mml:mi><mml:mrow><mml:mtext>VP</mml:mtext></mml:mrow></mml:msub></mml:mrow><mml:mrow><mml:mn>2</mml:mn><mml:mo stretchy='false'>(</mml:mo><mml:mn>1</mml:mn><mml:mo>&#x02212;</mml:mo><mml:mi>&#x003B6;</mml:mi><mml:mo stretchy='false'>)</mml:mo></mml:mrow></mml:mfrac></mml:mrow></mml:math></inline-formula>. The term depends linearly on the number of synapses per virtual process. A fraction of &#x003B6; &#x0003D; 0.95 is thus already reached when the network size <italic>N</italic><sub>&#x003B6;</sub> &#x02243; <italic>K</italic><sub>VP</sub>/(2&#x000B7;0.05) &#x0003D; 10 <italic>K</italic><sub>VP</sub> exceeds the number of local synapses by one order of magnitude independent of the other parameters. This result is independent of the detailed parameters of the memory model, as it results from the generic combinatorics of the problem.</p>
<p>The effects on the memory requirements can be observed in Figure <xref ref-type="fig" rid="F2">2</xref> (top), where the amount of memory consumed by lists of length one and larger one are shown separately: at <italic>MT</italic> &#x0007E; 10<sup>4</sup> the memory consumed by the former starts exceeding the consumption of the latter. At <italic>MT</italic> &#x0007E; 10<sup>5</sup> the memory consumed by lists with more than one synapse is negligible. As we have seen in Section 2.5, the scenario at <italic>MT</italic> &#x0007E; 10<sup>5</sup> is the relevant one for currently available supercomputers. In the following we use the analysis above to guide our development of memory-efficient data structures on such machines. Figure <xref ref-type="fig" rid="F2">2</xref> (top) highlights that the intermediate synaptic infrastructure when storing only a single synapse must be lean. In the 3g kernel the memory consumed by a single synapse object is 48 B, while the overhead of the intermediate infrastructure is 136 B. Hence in the limit of sparseness, a synapse effectively costs 48 B &#x0002B; 136 B. Reducing the contribution of the intermediate infrastructure is therefore the first target of our optimizations described in Section 3.2. We identify the size of the synapse objects as the contribution of secondary importance and describe in Section 3.3 the corresponding optimization. The resulting small object sizes can only be exploited with a dedicated pool allocator (see Section 3.4). The least contribution to the memory footprint stems from the neuronal infrastructure, the improved design of which is documented in Section 3.5. The sparse table has an even larger contribution than the neuronal infrastructure. However, the employed collective communication scheme that transmits the occurrence of an action potential to all other machines requires the information whether or not the sending neuron has a target on a particular machine. This information is represented close to optimal by the sparse table. In the current work we therefore refrained from changing this fundamental design decision.</p>
</sec>
<sec>
<title>3.2. Auto-adjusting connection infrastructure</title>
<p>Figure <xref ref-type="fig" rid="F3">3A</xref> illustrates the connection infrastructure of NEST in the 3rd generation kernel (3g). As shown in Section 3.1 this data structure produces an overhead in the limit of virtual processes <italic>MT</italic> exceeding the number of outgoing synapses <italic>K</italic> per neuron; a presynaptic neuron then in most cases establishes zero or one synapse on a given core. The overhead can be avoided, because the intermediate data structure is merely required to distinguish different types of synapses originating from the same source neuron. For only a single outgoing synapse per source neuron it is not required to provide room to simultaneously store different types.</p>
<p>The main idea is hence to use data structures that automatically adapt to the stored information, as illustrated in Figure <xref ref-type="fig" rid="F3">3B</xref>. The algorithm wiring the network then chooses from a set of pre-defined containers depending on the actual need. The corresponding data types are arranged in a class hierarchy shown in Figure <xref ref-type="fig" rid="F4">4</xref>, with the abstract base class <monospace>ConnectorBase</monospace> defining a common interface. The wiring algorithm distinguishes four cases, depending on the number and types of the outgoing synapses of the given source neuron:</p>
<list list-type="simple">
<list-item><p><bold>Case 0</bold> The source neuron has no target on this machine. In this case, the unset bit in the sparse table (see Figure <xref ref-type="fig" rid="F3">3</xref>) indicates the absence of synapses and no further data structures are created.</p></list-item>
<list-item><p><bold>Case 1</bold> The source neuron has outgoing synapses that are of the same type. In this case we use a type-homogeneous container <monospace>HomConnector</monospace>. Depending on the number of synapses, we use two different strategies to implement the homogeneous container. If less than <italic>K</italic><sub>cutoff</sub> synapses are stored, we employ a recursive C&#x0002B;&#x0002B; template definition of a structure that holds exactly 1, 2, &#x02026;, <italic>K</italic><sub>cutoff</sub> synapses. Here <italic>K</italic><sub>cutoff</sub> is a compile-time constant that throughout this work was chosen to be <italic>K</italic><sub>cutoff</sub> &#x0003D; 3. The recursive template definition is shown in Algorithm 1 and follows the known pattern defining the recursion step with an integer-dependent template and the recursion termination by a specialization for one specific integer value (Vandervoorde and Josuttis, <xref ref-type="bibr" rid="B45">2003</xref>, Ch. 17). The classes are instantiated at compile time due to the recursive definition of the method <monospace>push_back</monospace>. The set of containers implements the functionality of a vector, requiring just the memory for the actual payload plus an overhead of 8 B for the virtual function table pointer due to the use of an abstract base class providing the interface of virtual functions. Our implementation uses a custom-made pool allocator ensuring that each thread allocates from its own contiguous block of memory to improve the cache performance and to reduce the overhead of allocating many small objects (see Section 3.4).</p></list-item>
<list-item><p><bold>Case 2</bold> If more than <italic>K</italic><sub>cutoff</sub> synapses of the same type are stored we resort to the conventional implementation employing a <monospace>std::vector</monospace> from the C&#x0002B;&#x0002B; standard template library. The implementation of a vector entails an additional overhead of 3 times 8 B. This case provides the recursion termination for the set of homogeneous containers, as shown in Algorithm 1.</p></list-item>
<list-item><p><bold>Case 3</bold> If a source neuron has synapses of different types targeting neurons on the same machine, we employ the container <monospace>HetConnector</monospace>. This intermediate container stores several homogeneous connectors (of either Case 1 or 2 above) and is inherited from a <monospace>std::vector&#x0003C;ConnectorBase<sup>&#x0002A;</sup>&#x0003E;.</monospace></p></list-item>
</list>
<fig id="F4" position="float">
<label>Figure 4</label>
<caption><p><bold>Simplified class diagram of the connection infrastructure</bold>. The base class <monospace>ConnectorModel</monospace> serves as a factory for connections. The member function <monospace>add_connection</monospace> is called when a new synapse is created and implements the algorithm to select the appropriate storage container (see also Algorithm 2). The derived template <monospace>GenericConnectorModel</monospace> contains all code independent of synapse type and is instantiated once for each synapse type. It also holds the set of default values for synapse parameters (data member <monospace>default_connection</monospace>) and those parameters that are the same for all synapses of the respective type (data member <monospace>cp</monospace>). The class <monospace>ConnectorBase</monospace> defines the interface for all synapse containers, providing abstract members to deliver spike events (<monospace>send</monospace>), to determine the synapse type of type-homogeneous containers (<monospace>get_syn_id</monospace>), and to test if a container is type-homogeneous, i.e., whether it stores a single or several different synapse types (<monospace>homogeneous_model</monospace>). Two derived classes implement type-homogeneous containers (<monospace>HomConnector</monospace>) and type-heterogeneous containers (<monospace>HetConnector</monospace>). The latter, in turn, may contain several containers of class <monospace>HomConnector</monospace>, stored in a C&#x0002B;&#x0002B; standard library vector (by double inheritance from <monospace>std::vector</monospace>). <monospace>HomConnector</monospace> inherits the function <monospace>push_back</monospace> from the interface <monospace>vector_like</monospace>, required to implement the recursive sequence of container types, described in Algorithm 1.</p></caption>
<graphic xlink:href="fninf-08-00078-g0004.tif"/>
</fig>
<p>The algorithm for creating new connections employing these adaptive data containers is documented as pseudo-code in Algorithm 2.</p>
</sec>
<sec>
<title>3.3. Condensed synapse objects</title>
<p>In a typical cortical neuronal network model, the number of synapses exceeds the number of neurons by a factor &#x0007E; 10<sup>4</sup>. To reduce the memory consumption of a simulation, it is thus most efficient to optimize the size of synapse objects, outlined in our agenda at the end of Section 2.3 as step two of our optimizations. We therefore reviewed the <monospace>Connection</monospace> class (representing the synapses) and identified data members that could be represented more compactly or which could even be removed. In refactoring the synapse objects our objective is to neither compromise on functionality nor on the precision of synaptic state variables, such as the synaptic weight or the spike trace variables needed for spike-timing dependent plasticity (Morrison et al., <xref ref-type="bibr" rid="B32">2007</xref>); these state variables are still of type <monospace>double</monospace> in the new simulation kernel (4g). Our analysis of the <monospace>Connection</monospace> data structure identified three steps to further reduce the memory consumption of single synapse objects. The steps are explained in the following three subsections and concluded by a subsection summarizing the resulting reduced memory footprint.</p>
<sec>
<title>3.3.1. Avoidance of polymorphic synapse objects</title>
<p>As shown in Figure <xref ref-type="fig" rid="F4">4</xref> and in Algorithm 1, the container-classes are templates with the synapse type <monospace>connectionT</monospace> as a template parameter. Consequently, the container itself does not need a polymorphic interface, because by specialization for a particular synapse type this type is known and fixed at compile time. The only exception to this rule is synapse creation: we here need to check that the synapse model and the involved neuron models are compatible. More precisely, we need to ensure that (i) the new connection and (ii) the target node are able to handle the type of events sent by the source node. In NEST 2.2 (3g) the first of the two checks (i) requires a common interface to all synapse objects (i.e., an abstract base class <monospace>Connection</monospace>) that provides a set of virtual functions, one for each possible event type (spike events, voltage measurement requests, etc.). The synapse object then implements only those virtual member functions for event types it can handle. A similar pattern is used for the check (ii), determining whether the target node is able to handle the incoming event type. On a 64 bit architecture the virtual base class causes a per-object overhead of 8 B for the virtual function table pointer. Having such a pointer in each node hardly affects the memory consumption, but spending these additional 8 B per synapse object seems rather costly given the fact that the connection handshake is the only instance when NEST exploits the polymorphism of <monospace>Connection</monospace>. Therefore, in the 4g kernel we redesigned the handshaking algorithm such that it still makes use of the polymorphism of <monospace>Node</monospace> but no longer requires virtual functions in <monospace>Connection</monospace>. This reduces the per-synapse memory usage <italic>m</italic><sub>c</sub> by 8 B.</p>
<p>The design pattern that circumvents polymorphic synapse objects is derived from the visitor pattern (Gamma et al., <xref ref-type="bibr" rid="B16">1994</xref>; Alexandrescu, <xref ref-type="bibr" rid="B2">2001</xref>). A sequence diagram of the connection setup is shown in Figure <xref ref-type="fig" rid="F5">5</xref>. The crucial step is to shift the set of virtual functions that check the validity of received events from the synapse objects to a nested class, called <monospace>check_helper</monospace>. Each connection class owns its specific version of <monospace>check_helper</monospace>, which is derived from the <monospace>Node</monospace> base class. This inner class redefines the virtual function <monospace>handles_test_event</monospace> for those event types the connection model can handle. The default implementations inherited from the base class throw an exception and thus by default signal the inability of the synapse to handle the particular event. Since the connection class only contains the nested class definition, rather than a member of type <monospace>check_helper</monospace>, the nested class does not contribute to the memory footprint of the connection.</p>
<fig id="F5" position="float">
<label>Figure 5</label>
<caption><p><bold>Connection handshaking mechanism of the 4g kernel</bold>. By executing connect, NEST calls <monospace>add_connection</monospace> of the corresponding connector in the connection infrastructure. This function creates a new instance for the connection, sets its parameters and starts the connection handshaking by calling <monospace>check_connection</monospace>. This function creates an instance of the <monospace>check_helper</monospace> class and calls <monospace>send_test_event</monospace> of the source node twice to send test events to the synapse represented by <monospace>check_helper</monospace> and to the target node, respectively. Both instances execute <monospace>handles_test_event</monospace> which, if the event cannot be handled, ends in the base-class implementation throwing an exception.</p></caption>
<graphic xlink:href="fninf-08-00078-g0005.tif"/>
</fig>
<p>This new design has the additional advantage that checks (i) and (ii) have the same structure following the visitor pattern, shown in Figure <xref ref-type="fig" rid="F5">5</xref>: For check (i), the synapse creates an object of its corresponding type <monospace>check_helper</monospace>, passes it as a visitor to the source neuron&#x00027;s member function <monospace>send_test_event</monospace>, which in turn calls the overloaded version of the virtual function <monospace>handles_test_event</monospace> that has the matching event type. The test passes if <monospace>handles_test_event</monospace> is implemented for the type of event <monospace>e</monospace>, shown for the example of a <monospace>SpikeEvent</monospace> in Figure <xref ref-type="fig" rid="F5">5</xref>. The second check (ii) proceeds along analogous lines. Here the target neuron is passed as a visitor to the source neuron&#x00027;s member function <monospace>send_test_event</monospace>, which in turn sends the event via a call to the target neuron&#x00027;s <monospace>handles_test_event</monospace> method.</p>
</sec>
<sec>
<title>3.3.2. Indexed target addressing</title>
<p>A connection has to store the information about its target node. In NEST 2.2 (3g) this was solved by storing a pointer to the target node, consuming 8 B on a 64 bit architecture. The new simulation kernel (4g) implements the target addressing flexibly via a template argument <monospace>targetidentifierT</monospace> to the connection base class. This template parameter enables the formulation of synapse objects independent of how the target is stored, as long as the <monospace>targetidentifierT</monospace> provides methods to obtain the actual target neuron&#x00027;s physical address via a member <monospace>get_target_ptr()</monospace>. Besides the original implementation that stores the target as a full pointer, the 4g kernel supports indexed addressing of the target neuron. The two addressing schemes correspond to different implementations of the target identifier. Each synapse type may be instantiated with either of them.</p>
<p>Indexed addressing makes use of the limited number of neurons that are local to a given virtual process. These nodes are stored in a vector of pointers required to update the neuronal dynamics. In a parallel simulation, the number of local neurons rarely exceeds &#x0007E; 10<sup>4</sup> nodes. The index space of local nodes is thus sufficiently covered by indices of 2 B length corresponding to a maximal index of 2<sup>16</sup> &#x02212; 1 &#x0003D; 65,535. The target identifier then stores the index of the target neuron corresponding to its position in the vector of VP-local nodes. We call this index &#x0201C;thread-local id&#x0201D; in the following. Determining the target requires one more indirection to compute the actual address from the stored index, but saves 6 B of memory per target pointer. The implementation requires an extension of the user interface, because in the 3g kernel the thread-local vectors of nodes are only generated dynamically at the start of the simulation. The translation from the global id (GID) of a target neuron to the thread-local id is hence unavailable during wiring of the network. To employ the indexed addressing scheme the user therefore needs to execute the function <monospace>CreateThreadLocalIds</monospace> after all nodes have been created and prior to the creation of any synapse using the indexed addressing scheme. This function fills the vectors holding the thread-local neurons and moreover stores the thread-local id for each neuron in its base class. The latter information is needed during connection setup to obtain the thread-local id from a given global neuron id.</p>
</sec>
<sec>
<title>3.3.3. Combined storage of delay and synapse type</title>
<p>In a third step, the storage of the synapse type (<monospace>syn_id</monospace>) and the delay (d) of a connection are optimized. The 3rd generation connections stored these properties separately, the delay as a long integer variable with 8 B and the synapse type as an unsigned integer with 4 B length. The delay is represented as an integer multiple of the chosen simulation resolution <italic>h</italic>, typically <italic>h</italic> &#x0003D; 0.1 ms (Morrison et al., <xref ref-type="bibr" rid="B35">2005b</xref>). A reduction of the storage size from 8 B to 3 B hence corresponds to a restriction of the maximum delay (assuming <italic>h</italic> &#x0003D; 0.1 ms) from 2<sup>64</sup> <italic>h</italic> &#x02243; 5.8 &#x000B7; 10<sup>7</sup> years to 2<sup>24</sup> <italic>h</italic> &#x02243; 1670 s, which is more than sufficient for all practical demands. The limitation of the synapse type identifier to an unsigned int of 1 B limits the number of different synapse types to 2<sup>8</sup> &#x0003D; 256, a reasonable upper limit for different synapse models. To ensure that delay and synapse identifier are stored as compactly as possible independent of memory alignment choices of the compiler, both variables are stored as 8 bit and 24 bit fields (Stroustrup, <xref ref-type="bibr" rid="B41">1997</xref>, Ch. C.8.1) in a common structure requiring 4 B; the structure provides methods for convenient access to synapse id and delay. Overall, the new storage scheme thus requires just 4 B per synapse instead of 8 B &#x0002B; 4 B required by the 3rd generation kernel for the same information, saving 8 B of memory.</p>
</sec>
<sec>
<title>3.3.4. Memory footprint of synapse objects</title>
<p>The simplest type of synapse, the static synapse, is described completely by information identifying its target, type, delay and weight. In the 3g simulation kernel, the type required 4 B, while the three other items each required 8 B as described above, for a total of 28 B. On computers with 64 bit architecture, data types requiring 8 B, such as pointers, doubles and long integers, are by default aligned with 64 bit-word boundaries, so that a static synapse object in effect required 32 B: the 4 B unsigned integer representing the synapse type was padded to 8 B to ensure proper alignment of the following 8 B variable.</p>
<p>Combining all three 4g kernel optimizations described above, the size of a static synapse object shrinks to 2 B for the target index, 4 B for the combined synapse type id and delay, and 8 B for the synaptic weight (double precision). Provided that the data members in the synapse object are ordered as is <monospace>{target index, synapse type id &#x0002B; delay, synaptic weight}</monospace>, the object requires 16 B in total, including 2 B of padding inserted after the target index.</p>
<p>In addition to the member variables of a static synapse, an object representing an STDP synapse stores the synaptic trace variable (double) (Morrison et al., <xref ref-type="bibr" rid="B33">2008</xref>), consuming another 8 B. The total memory footprint for STDP synapse objects is thus 24 B, of which 22 B or 92% hold data, while the remaining 8% are padding. On some systems, the 2 B of padding might in principle be avoided by forcing the compiler to generate code for unaligned memory access, e.g., using the <monospace>__attribute__(packed)</monospace> provided by the GNU C&#x0002B;&#x0002B; compiler (Free Software Foundation, <xref ref-type="bibr" rid="B15">2013</xref>, Ch. 6.36). Unfortunately, such packing is not defined in the C&#x0002B;&#x0002B;-standard and thus compiler-dependent; on some architectures, unaligned memory access may also incur significant runtime overhead (Rentzsch, <xref ref-type="bibr" rid="B38">2005</xref>). Given that synapse objects contribute less than 50% to the total memory requirement of the simulator for large networks, as shown in Figure <xref ref-type="fig" rid="F2">2</xref>, the memory overhead incurred by padding is thus less than 4%. To ensure maximum portability, NEST does not use unaligned memory access.</p>
</sec>
</sec>
<sec>
<title>3.4. Pool allocator</title>
<p>The standard allocator on most systems has two severe problems with regard to memory locality when using multiple threads and memory overhead when allocating many small objects. The first problem stems from the allocator not taking into account the actual physical layout of the memory architecture of the machine. In particular, the memory for objects of different threads is often not separated, but objects are rather allocated in the order in which they are created. This makes caching on multi-core machines with different caches for different cores or with different memory banks for different cores (cf. ccNUMA) inefficient and leads to frequent (and slow) cache reloads from the main memory, a problem generally referred to as cache thrashing. The second problem is caused by the fact that the allocator needs to keep administrative information about each single allocation for freeing the memory and returning it to the operation system after its use. Usually, this information consists at least of a pointer to the data (8 B) and the size of the allocated memory (8 B). If the size of administrative data is in the range of the size of the allocation, this means a significant memory overhead.</p>
<p>To ameliorate these problems, we implemented a stateless custom allocator, which does not keep any administrative information about allocations and provides thread-local memory pools for the storage of connection containers (see Stroustrup, <xref ref-type="bibr" rid="B41">1997</xref>, Ch. 19.4.2 for the basic concept). The absence of allocation information is not a problem, as the data structures for the storage of synapses only grow, and never are freed during the run of the program. Moreover, as shown in Section 3.1, in the sparse limit reallocation of target lists is rare, as most target lists contain only a single synapse. In this limit the simple pool allocator is hence close to optimal. The use of the pool allocator needs to be selected by the user with a compile-time switch. On small clusters or desktop machines, where frequent reallocation takes place, the standard allocator is recommended.</p>
</sec>
<sec>
<title>3.5. Sparse node array</title>
<p>Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>) showed that the memory overhead required to represent neurons on each MPI process could be reduced considerably by using a sparse table. In such a table, each non-local neuron is represented by a single bit, while the overhead for local neurons is only a few Bytes. Kunkel et al. (<xref ref-type="bibr" rid="B29">2012b</xref>) give the following expression for the memory required to represent neurons:</p>
<graphic xlink:href="fninf-08-00078-i0005.tif"/>
<p>Using the sparse table, one has the following parameters: <italic>m</italic><sup>0</sup><sub>n</sub> &#x0003D; <inline-formula><mml:math id="M38"><mml:mrow><mml:mfrac><mml:mn>1</mml:mn><mml:mn>3</mml:mn></mml:mfrac></mml:mrow></mml:math></inline-formula> B, <italic>m</italic><sup>&#x02205;</sup><sub>n</sub> &#x0003D; 0 B, <italic>m</italic><sup>&#x0002B;</sup><sub>n</sub> &#x0003D; 24 B, and <italic>m</italic><sub>n</sub> &#x02248; 1000 B, where the first three parameters describe overhead, while the last parameter represents actual neuron objects. With <italic>N</italic>/<italic>M</italic> &#x0007E; 10<sup>3</sup> neurons per MPI process, neuron objects consume &#x0007E; 1 MB, while the overhead from Equation (14) is</p>
<graphic xlink:href="fninf-08-00078-i0006.tif"/>
<p>For <italic>N</italic>/<italic>M</italic> &#x0007E; 10<sup>3</sup>, the second term is about 24 kB and thus negligible, while the first term becomes appreciable for large networks. Indeed, for <italic>N</italic> &#x0003D; 10<sup>8</sup> neurons, it amounts to approximately 32 MB, for <italic>N</italic> &#x0003D; 10<sup>9</sup> to 318 MB, almost all of which are consumed for zero-bits in the bit array of the sparse table. This not only requires appreciable amounts of memory to represent vanishing amounts of information, it also means that sparse table lookups, which in most cases return negative results, become cache inefficient, as the size of the bit array by far exceeds the cache size.</p>
<p>In the following we show how this overhead can be eliminated entirely. The basic idea is to exploit the round-robin distribution of neurons to virtual processes and thus MPI processes (Eppler, <xref ref-type="bibr" rid="B13">2006</xref>; Plesser et al., <xref ref-type="bibr" rid="B36">2007</xref>). As NEST builds a network, neurons are created with strictly increasing global neuron ids (GIDs) <italic>g</italic>, and each neuron is assigned to and stored on MPI rank</p>
<disp-formula id="E16"><label>(16)</label><mml:math id="M39"><mml:mrow><mml:msub><mml:mi>m</mml:mi><mml:mi>g</mml:mi></mml:msub><mml:mo>=</mml:mo><mml:mi>g</mml:mi><mml:mtext>&#x02009;</mml:mtext><mml:mi>mod</mml:mi><mml:mtext>&#x02009;</mml:mtext><mml:mi>M</mml:mi><mml:mtext>&#x02009;</mml:mtext><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Thus, if we place the neurons assigned to a single MPI process in an array in order of creation, GIDs of neighboring neurons will differ by <italic>M</italic>. For any given <italic>g</italic>, we thus can use linear interpolation between the GIDs of the first and last local neuron to determine the index pertaining to that GID in the array of local neurons. We then only need to check whether the neuron found at that index has the correct GID (i.e., is local to the MPI process) or not, in the latter case we conclude that the neuron is managed by a different MPI process.</p>
<p>Unfortunately, reality is slightly more complicated. Certain network elements are replicated on all virtual processes in NEST, either of logical necessity (subnet nodes used to structure large networks) or for performance reasons (stimulating and recording devices); we refer to such network elements as replicated nodes. This means that (i) any node <italic>g</italic> is guaranteed to be local to MPI rank <italic>m</italic><sub><italic>g</italic></sub> according to Equation (16), (ii) if node <italic>g</italic> is a replicating node, it is also local to all other MPI ranks, and thus (iii) neighbors in the array of nodes are not necessarily spaced in intervals of <italic>M</italic>. This will skew the linear interpolation used to look up nodes by their GIDs.</p>
<p>Fortunately, the number of replicating nodes must be small in any practical simulation, because the memory requirement of the simulation would not scale otherwise. Thus, the skew will be small. This suggests the following approach: Let <italic>n</italic><sub>loc</sub> be the number of local nodes for a given MPI rank and <italic>g</italic><sub>loc,min</sub> and <italic>g</italic><sub>loc,max</sub> the smallest and largest local GID, respectively, (we ignore that the root network with GID 0 is local to all ranks), and define the scaling factor</p>
<disp-formula id="E17"><label>(17)</label><mml:math id="M40"><mml:mrow><mml:mi>&#x003B1;</mml:mi><mml:mo>=</mml:mo><mml:mfrac><mml:mrow><mml:msub><mml:mi>n</mml:mi><mml:mrow><mml:mtext>loc</mml:mtext></mml:mrow></mml:msub><mml:mo>&#x02212;</mml:mo><mml:mn>2</mml:mn></mml:mrow><mml:mrow><mml:msub><mml:mi>g</mml:mi><mml:mrow><mml:mtext>loc</mml:mtext><mml:mo>,</mml:mo><mml:mtext>&#x0200A;max</mml:mtext></mml:mrow></mml:msub><mml:mo>&#x02212;</mml:mo><mml:msub><mml:mi>g</mml:mi><mml:mrow><mml:mtext>loc</mml:mtext><mml:mo>,</mml:mo><mml:mtext>&#x0200A;min</mml:mtext></mml:mrow></mml:msub></mml:mrow></mml:mfrac><mml:mtext>&#x02009;</mml:mtext><mml:mo>.</mml:mo></mml:mrow></mml:math></disp-formula>
<p>Then</p>
<disp-formula id="E18"><label>(18)</label><mml:math id="M41"><mml:mrow><mml:msub><mml:mi>l</mml:mi><mml:mi>g</mml:mi></mml:msub><mml:mo>*</mml:mo><mml:mo>=</mml:mo><mml:mrow><mml:mo>&#x0230A;</mml:mo><mml:mrow><mml:mn>1</mml:mn><mml:mo>+</mml:mo><mml:mi>&#x003B1;</mml:mi><mml:mo stretchy='false'>(</mml:mo><mml:mi>g</mml:mi><mml:mo>&#x02212;</mml:mo><mml:msub><mml:mi>g</mml:mi><mml:mrow><mml:mtext>loc</mml:mtext><mml:mo>,</mml:mo><mml:mtext>&#x0200A;min</mml:mtext></mml:mrow></mml:msub><mml:mo stretchy='false'>)</mml:mo></mml:mrow><mml:mo>&#x0230B;</mml:mo></mml:mrow></mml:mrow></mml:math></disp-formula>
<p>provides a linear estimate of the index of the node with GID <italic>g</italic> in the local array of nodes. This estimate is exact if we have a single MPI process or there are no replicating nodes except the root network. We can thus look up nodes by starting our search at index <italic>l</italic><sup>&#x0002A;</sup><sub><italic>g</italic></sub> in the array of local nodes and then proceed to smaller or larger nodes until we either have found the node with GID <italic>g</italic> or reached a node with GID smaller (larger) than <italic>g</italic>, in which case we conclude that node <italic>g</italic> is not local. The complete algorithm is given as Algorithm 3.</p>
<p>The underlying data structure is the <monospace>SparseNodeArray</monospace>: Its main component is a C&#x0002B;&#x0002B; <monospace>vector</monospace> that stores in each element the pointer to a node and the GID of that node. This ensures that the linear search from <italic>l</italic><sup>&#x0002A;</sup><sub><italic>g</italic></sub> is cache efficient, compared to a <monospace>vector</monospace> keeping node pointers only, which would require looking up the GID via the node pointer in each search step. Additionally, the <monospace>SparseNodeArray</monospace> keeps track of the largest GID in the entire network (to detect invalid GIDs), the smallest and largest local GID and the scaling factor &#x003B1;. The memory overhead for this data structure is thus one pointer and one long per <italic>local</italic> node, i.e.,</p>
<graphic xlink:href="fninf-08-00078-i0007.tif"/>
<p>The overhead now only depends on the number of neurons per process, <italic>N</italic>/<italic>M</italic>, but no longer directly on the total number of neurons in the network, <italic>N</italic>.</p>
<p>To evaluate the quality of the linear estimate provided by Equation (18), we counted the number of search steps required to find the correct node using a specially instrumented version of the NEST code. We collected data for <italic>M</italic> &#x0003D; 2048 to <italic>M</italic> &#x0003D; 65,536 MPI processes with four threads each and between 550 and 230 nodes per thread. The average number of search steps is 0.7 in this data and lookup never requires more than two steps for over 3 billion analyzed lookups. Therefore, we consider the linear estimate to be close to optimal. In addition to the lookup by GID, the <monospace>SparseNodeArray</monospace> also provides an interface for direct iteration solely over the local nodes to allow for, e.g., efficient initialization of all nodes.</p>
</sec>
<sec>
<title>3.6. Performance of 4g technology</title>
<p>Figure <xref ref-type="fig" rid="F6">6</xref> shows a strong scaling for the 4g kernel. At the left-most point the workload per core is maximal; the number of neurons is chosen such that the memory is completely filled. Near the point of maximum workload the scaling on JUQUEEN is close to optimal, but degrades for lower workload. This is expected as ultimately the serial overhead dominates the simulation time. The relevance of a strong scaling graph is therefore naturally limited. To minimize the queuing time and the energy consumption it is desirable to choose the smallest possible machine size that enables the simulation of a given problem size. In the following we will hence study maximum-filling scalings, where for a given machine size <italic>MT</italic> we simulate the largest possible network that completely fills the memory of the machine. The procedure to arrive at the maximum possible network size is described in Section 2.6.</p>
<fig id="F6" position="float">
<label>Figure 6</label>
<caption><p><bold>Strong scaling on K computer and JUQUEEN</bold>. Solid curves show the scaling of simulation time, dashed curves show setup time for networks of <italic>N</italic> &#x0003D; 5,242,880 neurons on JUQUEEN (blue) and <italic>N</italic> &#x0003D; 5,210,112 on the K computer (red). Black dotted lines are the linear expectations for simulation and setup. All simulations were carried out using the parameters of set 1 (cf. Section 2.2).</p></caption>
<graphic xlink:href="fninf-08-00078-g0006.tif"/>
</fig>
<p>The reduction of the memory consumption of the 4g kernel compared to the 3g kernel is shown in Figure <xref ref-type="fig" rid="F7">7</xref>. At a given machine size <italic>MT</italic> the same network is simulated with both kernels. For each <italic>MT</italic> the size of the network is chosen such that the 3g simulation consumes all available memory on the K computer (maximum-filling scaling). At high numbers of cores around <italic>MT</italic> &#x0007E; 100,000 the 4g kernel reduces the required memory by a factor of more than 3, for smaller machine sizes the reduction of memory consumption is less, but still substantial.</p>
<fig id="F7" position="float">
<label>Figure 7</label>
<caption><p><bold>Comparison of memory usage of 3g and 4g technology</bold>. Black triangles show the maximum possible network size, using parameters of set 1 (cf. Section 2.2), that can be simulated on the K computer and on JUQUEEN when using the 3g technology with <italic>T</italic> &#x0003D; 8 threads per compute node (left vertical axis). The dotted black line indicates the ideal case where the network size increases with the same factor as the number of virtual processes. Circles show the memory consumption (right vertical axis) of simulations with the 3g (open) and 4g (filled) technology on the K computer (red) and JUQUEEN (blue), respectively.</p></caption>
<graphic xlink:href="fninf-08-00078-g0007.tif"/>
</fig>
<p>Figure <xref ref-type="fig" rid="F8">8</xref> shows the time to setup the network (top panel) and the simulation time (bottom panel) for the 3g and the 4g kernel in the same maximum-filling scaling as in Figure <xref ref-type="fig" rid="F7">7</xref>. Although both kernels simulated the same network at a given <italic>MT</italic> and hence the same computation takes place in both simulations (same update steps of neurons and synapses, same activity, etc.), the run time of the new kernel is typically reduced especially at large machine sizes. On JUQUEEN this reduction monotonically increases with machine size, on the K computer the simulation time exhibits fluctuations that presumably originate from different load levels caused by other users of the machine, potentially occluding a clear monotonic dependence. The faster simulation time of the new kernel points at the random memory access as an important contribution to the computation time. The smaller objects of the connection infrastructure enable more efficient use of the cache and reduce the overall required memory bandwidth.</p>
<fig id="F8" position="float">
<label>Figure 8</label>
<caption><p><bold>Comparison of performance of 3g and 4g technology</bold>. Setup time (upper panel) and simulation time (lower panel) as a function of number of virtual processes for the maximum network size that can be simulated when using the 3g technology (see Figure <xref ref-type="fig" rid="F7">7</xref>) and parameter set 1 (cf. Section 2.2). Circles refer to simulations with 3g (open) and 4g (filled) technology, respectively. Results are shown for the K computer (red) and JUQUEEN (blue) using <italic>T</italic> &#x0003D; 8 threads per compute node.</p></caption>
<graphic xlink:href="fninf-08-00078-g0008.tif"/>
</fig>
<p>The 4g implementation exhibits a reduction in setup time by a factor of 2&#x02013;8 depending on network size (Figure <xref ref-type="fig" rid="F8">8</xref>, top panel). In parts this higher performance is due to ongoing conventional optimization of the wiring routines; for example, by representing consecutive neuron ids within the wiring process by the beginning and end of the range (4g) instead of by explicitly naming all elements (3g). In parts the difference is due to faster memory allocation through the dedicated pool allocator and the smaller objects representing synapses and connection infrastructure.</p>
<p>The comparison of the 3g and 4g kernels in Figures <xref ref-type="fig" rid="F7">7</xref>, <xref ref-type="fig" rid="F8">8</xref> is based on the maximum network size the 3g kernel can represent on a given number of cores. The reduced memory consumption of the 4g kernel allows us to simulate larger networks with the same computational resources. In Figure <xref ref-type="fig" rid="F9">9</xref> we therefore show a maximum-filling scaling determined for the 4g kernel, showing the maximum network size that can be simulated with a given number of cores <italic>MT</italic> and hence for a given amount of working memory. The growth of network size <italic>N</italic> with <italic>MT</italic> stays close to the ideal line.</p>
<fig id="F9" position="float">
<label>Figure 9</label>
<caption><p><bold>Maximum network size and corresponding run time as a function of number of virtual processes</bold>. Triangles show the maximum network size that can be simulated with parameter set 1 (cf. Section 2.2) on the K computer (red) and on JUQUEEN (blue) when using the 4g technology with <italic>T</italic> &#x0003D; 8 threads per compute node. The dotted black line indicates the ideal case where the network size increases with the same factor as the number of virtual processes. Dark blue (JUQUEEN) and orange (K) triangles represent the maximum network size using all compute nodes and parameter set 2 (JUQUEEN: 1.08 &#x000B7; 10<sup>9</sup> neurons, K: 1.86 &#x000B7; 10<sup>9</sup> neurons). Filled circles show the corresponding wall-clock time required to simulate the network for 1 s of biological time.</p></caption>
<graphic xlink:href="fninf-08-00078-g0009.tif"/>
</fig>
<p>Using parameter set 2 with <italic>K</italic> &#x0003D; 6000 synapses per neuron and employing all 82,944 nodes of the K computer simultaneously in a single simulation with 8 cores each, we reached a maximum network size of 1.86 &#x000B7; 10<sup>9</sup> neurons and a total of 11.1 &#x000B7; 10<sup>12</sup> synapses. This is the largest simulation of a spiking neuronal network reported so far (RIKEN BSI, <xref ref-type="bibr" rid="B39">2013</xref>). This world-record simulation was performed on K, because only this machine provides the necessary memory to represent all synapses of the simulation with generic connectivity. Access to JUQUEEN and its predecessor JUGENE was, however, crucial for the design and implementation of the simulation kernel during the development phase of the K computer and for performing smaller simulations testing the implementation (Diesmann, <xref ref-type="bibr" rid="B10">2012</xref>). In terms of memory, the K computer is at the time of writing the second largest computer (1.4 PB RAM, on the nodes available here 1.3 PB), exceeded only by the IBM sequoia computer (1.5 PB) at the Lawrence Livermore National Laboratory. JUQUEEN provides about one-third (0.46 PB) of the memory of the former two systems. Previous to the current report, the largest spiking network simulation comprised 1.62 &#x000B7; 10<sup>9</sup> neurons with on average about 5700 synapses per neuron (Ananthanarayanan et al., <xref ref-type="bibr" rid="B3">2009</xref>). The simulation required less than 0.144 PB of memory by making use of a specific modular connectivity structure of the network. Thus, in contrast to the case of an arbitrary network discussed in the present study, the choice of a specific structure enabled the authors to condense the memory components of the connectivity infrastructure, corresponding in our implementation to the sparse table, the intermediate infrastructure, and the synapses (see Figure <xref ref-type="fig" rid="F2">2</xref>), into effectively only 16 B per synapse.</p>
<p>Comparing the theoretical prediction of the memory model to the empirically found maximum network size reveals that the theory underestimates the actual memory consumption. As shown in our previous work (Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>) these deviations are most likely due to underestimation of the object sizes. A direct comparison of the memory consumption of <italic>n</italic> objects of size <italic>m</italic> to the predicted value <italic>nm</italic> typically uncovers non-optimal object alignment. Obtaining instead the effective object sizes by linear regression, as in our earlier work (Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>), would decrease the deviation of the model from the empirical result.</p>
<p>Comparing the memory resources required by the 3g and 4g kernels as a function of the just fitting network size (Figure <xref ref-type="fig" rid="F10">10</xref>) shows the new kernel to be closer to the optimal linear scaling: doubling the machine size nearly doubles the network size that can be simulated. At <italic>MT</italic> &#x0003D; 196,608 virtual processes, the maximum network size possible with the 4g kernel is <italic>N</italic><sup>4g</sup><sub>max</sub> &#x0003D; 5.1 &#x000B7; 10<sup>8</sup> compared to <italic>N</italic><sup>3g</sup><sub>max</sub> &#x0003D; 1.0 &#x000B7; 10<sup>8</sup> for the 3g kernel. The absolute simulation time increases in the maximum-filling scheme for the 4g kernel compared to the 3g kernel, because the higher number of neurons per core constitutes a proportionally larger workload per core.</p>
<fig id="F10" position="float">
<label>Figure 10</label>
<caption><p><bold>Comparison of maximum-filling scaling for 3g and 4g</bold>. Required number of cores <italic>MT</italic> (black, left vertical axis; numbers quadrupling between ticks) and time (colors, right vertical axis) as a function of network size for a maximum filling with parameter set 1 (cf. Section 2.2) for the 3g kernel (open symbols) and the 4g kernel (filled symbols). Benchmarks on the K computer shown in red, on JUQUEEN in blue. The dashed black line indicates the ideal case where the required number of cores increases only with the same factor as the network size.</p></caption>
<graphic xlink:href="fninf-08-00078-g0010.tif"/>
</fig>
<p>For a fair comparison we therefore show in Figure <xref ref-type="fig" rid="F11">11</xref> the product of the runtime and the number of cores. The 4g technology shows a smaller slope of the dependence of the required resources on network size. This quantity is also of interest for the estimation of resources when planning research projects and applying for the required computation time as core hours are a commonly used unit in such documents. The duration of the simulation used for these benchmarks is 1 s of biological time. For longer times the core hours can be multiplied by the corresponding factor to obtain an estimate of the required resources. For example, given the resources of 10,000 core hours on the K computer we can perform a simulation of 1 s of biological time with about 7 &#x000B7; 10<sup>7</sup> neurons using the 3g kernel but with more than twice as many neurons (1.5 &#x000B7; 10<sup>8</sup>) using the 4g kernel.</p>
<fig id="F11" position="float">
<label>Figure 11</label>
<caption><p><bold>Required resources to simulate a network of size <italic>N</italic></bold>. Core hours are defined as <italic>MT</italic> &#x000D7; <italic>T</italic><sub>sim</sub> and do not account for idle cores of a node. Filled circles correspond to the 4g technology (see Figure <xref ref-type="fig" rid="F9">9</xref>) and open circles to the 3g technology (see Figure <xref ref-type="fig" rid="F7">7</xref> for maximum network size and lower panel of Figure <xref ref-type="fig" rid="F8">8</xref> for core hours). Results for K computer (red) and JUQUEEN (blue) using parameter set 1 (cf. Section 2.2). The dashed black line indicates the ideal case where the required resources increase with the same factor as the network size.</p></caption>
<graphic xlink:href="fninf-08-00078-g0011.tif"/>
</fig>
</sec>
</sec>
<sec sec-type="discussion" id="s4">
<title>4. Discussion</title>
<p>The purpose of this study is not to gain neuroscientific insight, but to provide the technology to carry out full-scale neuronal network simulations at the resolution of neurons and synapses on presently available supercomputers. The work addresses the problem of efficiently representing a neuronal network on a supercomputer in a distributed manner, so that high performance is achieved while maintaining flexibility and generality. Starting from the existing simulation technology and our previous work on a mathematical model of the memory consumption (Kunkel et al., <xref ref-type="bibr" rid="B29">2012b</xref>) of neural simulation codes, we employ and extend the analysis of memory consumption to expose a generic problem of a combinatorial nature. The problem can be summarized as follows: The number of interaction partners (synapses) of each element (neuron) is fixed by nature (about 10<sup>4</sup>), but the number of employed compute nodes may vary over many orders of magnitude, from a laptop (order 1) to a petascale supercomputer (order 10<sup>5</sup>). On supercomputers and for general connectivity of the network we inevitably face the problem of sparsity, where a neuron has a target on a given compute node only with small probability. Conversely, in a single core simulation, each neuron has all its targets on the very same core. Hence each end of this range requires different data structures for an effective representation of the synapses. On supercomputers this data structure must be distributed to cater for the memory demands of full-scale network simulations at cellular resolution. In the limit where each neuron only has none or a single synapse on a given machine the present data structure (Helias et al., <xref ref-type="bibr" rid="B21">2012</xref>) exhibits a two-fold redundancy: the amount is fixed (just 1) and the type of the synapse is unique. We here describe a novel data structure that allows full flexibility if a neuron has multiple targets on a local machine, but that collapses to a static data structure in the limit of a single target. We also redesigned the underlying synapse data structure and the connection algorithm to reduce the memory footprint of a synapse, and finally developed a new data structure to contain local nodes along with a corresponding look-up algorithm exploiting the regularity in assigning neurons to machines.</p>
<p>The network model and the corresponding model of memory consumption presented here constitute a worst case scenario for memory consumption and communication in the sense that there is no structure which can be exploited to efficiently map the topology of the network to the topology of the computer. Moreover, in NEST neurons are distributed across virtual processes in a round-robin fashion, which allows for efficient static load balancing but prevents taking into account network structure. Kunkel et al. (<xref ref-type="bibr" rid="B28">2012a</xref>) investigate the case where a fraction of the incoming synapses has a local origin such as, for example, in a cortical network. The analysis shows that representing locally connected substructures on a subset of the available compute nodes reduces memory consumption. However, the advantage of such a topology-driven distribution scheme over a round-robin scheme diminishes with the adaptive connection infrastructure of the 4g simulation kernel as the novel data structures enable efficient storage of short target lists.</p>
<p>Simulation software on supercomputers often employs highly specialized and optimized code written in languages with a low degree of abstraction to maximally profit from machine-specific optimizations and optimizing compilers. We here meet the need for flexibility and generality with a different approach, employing features of a high-level object oriented language, such as metaprogramming and polymorphism as well as object-oriented design patterns to reduce the memory consumption, while maintaining flexibility, generality, and performance. Concretely, we use recursive C&#x0002B;&#x0002B; templates to formulate a container class with similar functionality as a vector but only one third of the overhead in the limit of only a few entries. An abstract base class provides a common interface to the data structure that allows nesting of containers whenever needed, e.g., to distinguish different synapse types. We swap out the polymorphism from synapse objects to a nested helper class to avoid memory overhead due to the virtual function pointer, but yet have polymorphic behavior at hand to implement a handshaking mechanism at connection setup. We formulate the connection framework in general terms as templates with the synapse type and the target identifier as template parameters. This separates the infrastructure from the neuroscientifically interesting part of the code, that represents the synaptic dynamics. This separation eases the extension of the simulator by new synapse types. Different implementations of the target pointer (full pointer, indirect indexed addressing) can be combined flexibly with the same code for a synapse by changing one additional template parameter. The templates hence enable code generation for synapses in different combinations with types fixed at compile time, so that the interesting, and user extensible code of synaptic dynamics exists only once.</p>
<p>The resulting data structures reduce the memory consumption on a supercomputer by up to a factor of four. Not only is the memory footprint smaller, but the new memory layout also decreases both the time to connect and the time to simulate a network of a given size. The latter increase in performance is likely due to more efficient use of the cache. The same network can hence be simulated with fewer resources (core hours), or reversely, the same amount of resources enable the simulation of around twice as large networks with the 4g kernel compared to NEST 2.2 (3g). Thus, computing time awarded on supercomputers is used more efficiently. Also, a typical simulation run on a laptop benefits from the presented optimizations: simulating the model used for all benchmarks in this paper with 11,250 neurons and 6000 incoming synapses per neuron (parameter set 2) on a single core for a biological time of 1 s takes 35.5 s with both NEST 2.2 (3g) and the 4g kernel while the memory consumptions significantly decrease from 5.24 GB for NEST 2.2 to 3.11 GB for the 4g kernel. As a result of these theory-guided improvements to memory structures, the observed dominance of overhead has been eliminated.</p>
<p>For a given size of the computer, the new data structures enable the simulation of networks of larger size. Employing the entire K computer with all 8 cores of each processor, the network filling the effectively available memory of 1.07 PB of RAM has 1.86 &#x000B7; 10<sup>9</sup> neurons and 11.1 &#x000B7; 10<sup>12</sup> synapses, each represented with a double precision synaptic weight and STDP dynamics. This is the largest simulation to date in terms of connectivity. It took 793.42 s to build the network and 2481.66 s to simulate 1 s of biological time. The new technology can exploit the full size of JUQUEEN to simulate a network of 1.08 &#x000B7; 10<sup>9</sup> neurons with 6.5 &#x000B7; 10<sup>12</sup> synapses, a network in the range of a cat&#x00027;s brain.</p>
<p>The technology described in these pages is general in the sense that it can be used in any neuronal simulator satisfying the following three constraints: (1) neurons are atomic and simulated as a whole on a single node of a parallel machine, (2) synapses are stored on the nodes on which the postsynaptic neurons reside, and (3) spikes are communicated globally using, for example, collective MPI communication. In an application where the communication scheme ensures that each machine only receives spikes from neurons with local targets, the sparse table is not required, however, additional infrastructure is needed on the presynaptic side. The C2 simulator (Ananthanarayanan and Modha, <xref ref-type="bibr" rid="B4">2007</xref>), for example, employs such a directed communication; for a comparison of different spike exchange methods see Hines et al. (<xref ref-type="bibr" rid="B23">2011</xref>). In order to represent the connections between different compartments of a neuron distributed over several compute nodes, such as in the NEURON simulator (Hines et al., <xref ref-type="bibr" rid="B24">2008</xref>) or the Neural Tissue Simulator (Kozloski and Wagner, <xref ref-type="bibr" rid="B27">2011</xref>), the data structures presented here require major adaptation. In addition to the spike events also events mediating the instantaneous electrical interaction between compartments need to be communicated. If the number of coupled compartments is small compared to the number of compute nodes, we believe the metaprogramming technique to realize low-overhead vectors is still applicable. Related data structures may even be useful in other domains beyond neuroscience that face the problem of mapping a fixed number of heterogeneous interaction points on a number of CPU cores varying over many orders of magnitude. With exascale computers on the agenda, the ratio between interaction points and CPU cores is likely to drop further, increasing the sparsity of the problem.</p>
<p>Memory allocation can be improved beyond the solution presented here. In the limit of large numbers of compute nodes, we use a simple pool allocator for synapse objects that does not support reallocation of memory. In the limit where each neuron with high probability only has either zero or one target on a given machine this is not of practical concern, because target lists almost never grow beyond length one, so no reallocation is needed. In the intermediate regime, where target lists contain a few entries, an improved allocation scheme is beneficial. A drawback of the presented solution is that it requires the user to be aware of the simulation operating in the sparse regime. On a laptop, the allocator should be switched off, which is the default. In the regime of small and mid-size machines, an alternative to a pool allocator would be to determine the number of synapses ultimately generated in a preceding phase, such that the correct amount of memory can immediately be allocated. This approach, however, rules out the flexibility to implement structural plasticity and mechanisms of degeneration.</p>
<p>The benchmark information presented is required to apply for computation time grants, which typically request a proof of efficient use of the resources and a justification of the resources applied for. Strong scaling is one commonly requested measure. Our work shows that this measure is not relevant for all applications, in particular not for network simulations. The reason is that the typical neuroscientific use case of a supercomputer is close to the point of maximum filling, where the entire memory is used to represent the largest possible network on a given number of compute cores (see van Albada et al., <xref ref-type="bibr" rid="B44">2014</xref> for a discussion of further difficulties in assessing the performance of simulation codes for neuronal networks). The number of neurons per core is limited by the need to represent all synapses, rather than by the computation required to carry out the simulation. Reducing this number therefore further decreases the load of the CPUs. In strong scaling where the filling is systematically reduced, our benchmark simulations do not exhibit supra-linear scaling. Another measure occasionally required in applications for computation time is the number of executed floating point operations per second. For network models described here the absolute number of floating point operations in the code is small. The update of the neuronal state variables requires on the order of 10 floating point operations per time step and neuron. The majority of operations in network simulations as described here are related to random memory access caused by the delivery of the spiking events. Thus, without normalization for the fraction of floating point operations actually in the code, the relevance of the measure for neuronal network simulations is limited. In the extreme case of a code not using floating point numbers at all, say by the use of fixed point arithmetic or in case of a data base application, the floating point performance is exactly zero. Assessing the scalability of an application by weak scaling requires the problem size per machine node to be constant and in addition the problem to be representable in the memory available per node. Applications with computational overhead growing with machine size show the best weak scaling, if the problem size per node is maximized. In distributed neural simulations this overhead is dominated by communication. If memory overhead grows with machine or problem size (here: the employed sparse table), weak scaling requires the problem size to be chosen such that the problem still fits into memory at the largest machine size; at smaller machine sizes, there will hence be unused memory. In this study we employ maximum-filling scaling, using the maximal problem size per node at each machine size. For the 4g kernel this measure is very close to a weak scaling, as shown in Figure <xref ref-type="fig" rid="F9">9</xref> as the memory overhead is low. For the 3g kernel, however, the problem size depends sub-linearly on the machine size (Figure <xref ref-type="fig" rid="F7">7</xref>). Therefore, in the weak scaling scenario the scalability of the 3g kernel is worse than the one of the 4g kernel just because the smaller problem size reduces the workload per core such that the contribution of communication overhead to the total run time is more severe. We use maximum-filling scaling here for two reasons: (1) we aim at a harder comparison of the 4g kernel vs. the 3g kernel and (2) we are interested in a measure of the minimal resources (core hours) required to simulate a given neuroscientific problem. For our benchmark network this minimum is achieved at the point of maximum filling (data not shown). Deeper insight into the effectiveness the algorithm achieves in operating on the entities of the simulation, in our case the neurons, may be gained by normalizing the simulation times obtained for maximum filling by the workload, in our case neurons per core.</p>
<p>Computing time is typically accounted in units of the product of wall-clock time and either compute nodes or CPU cores. The benchmarks presented in this manuscript employ 8 cores per compute node, even though more cores are available per node on JUQUEEN. An accounting system measuring resources by the product of nodes and hours will therefore punish a memory intense application that, due to the small workload per core, does not employ all cores of a node. Nevertheless, assignment of the entire node to a single application is of course required due to the lack of memory for other applications.</p>
<p>When a supercomputer and not the personal workstation is the target of a new neuronal network model some further difficulties arise. Below is a brief account of our experience and the strategies we developed so far to cope with them. On novel supercomputing platforms, such as the K computer, provenance tracking of the simulation environment is particularly challenging, because of the co-development of the hardware, the operating system components, and the application software. Additional robustness of the workflow is required because the researcher maintaining the simulation script may not be the person submitting the jobs and collecting the results. Performing simulations on supercomputers is time consuming due to the long queuing time and errors in a simulation script can often only be detected by actually running the simulation. Moreover, finding the maximum network size that consumes all memory on a machine in an iterative trial-and-error fashion can consume considerable amounts of resources. We here followed a two step procedure, first obtaining from the memory-usage model a prediction of the maximum number of neurons fitting on a given portion of the machine, and then running a series of &#x0201C;dry runs,&#x0201D; executed only on a single node and mimicking the existence of the remainder of the network (see Section 2.6). An extension of the dry-run feature including the simulation phase is desirable, because it would allow the scientist to uncover errors in the simulation script during and after the simulation phase prior to the execution of the actual simulation and also because the memory consumption during simulation may depend on the actual dynamics due to the allocation of communication buffers. Moreover, the dynamics may depend on the network size, so that test runs of a reduced problem on a smaller machine do not result in an accurate estimate of the resources. Capturing such effects by a dry run, however, requires a problem-specific mathematical model of network activity, the absence of which may have motivated the simulation project in the first place.</p>
<p>Current simulation technology is still hampered by the bottleneck of writing the simulated data generated in parallel on many processors to disk. The employed standard file I/O is easily overburdened by on the order of 100,000 processors in parallel opening files. More research is needed on how to include general solutions developed for parallel I/O into the neuronal simulation technology or to develop alternative solutions collecting the data on a small number of processors before writing to disk.</p>
<p>The simulation times achieved by the current technology are sufficiently short to study dynamical features of neuronal networks on the time scale of seconds of biological time. Synaptic plasticity, however, requires biological times of minutes to hours, leading to wall-clock times in the range of 100&#x02013;10,000 h, which are typically beyond the resources available to a neuroscientist and impractical for exploratory research.</p>
<p>We present a viable solution to efficiently use the largest supercomputers available today for neuronal simulations. The exascale generation of supercomputers with even larger numbers of cores, however, is likely to require a new architecture. The mathematical analysis of memory consumption presented in the current work exposes the sparse table of neurons with local targets which has been the backbone of the communication algorithm since the first distributed neuronal simulations as the limiting component; it grows proportional to the total number of neurons in the network. As for each neuron one bit must be stored, indicating whether or not the corresponding neuron has a target on the given machine, the growth of the sparse table will ultimately limit the scalability of the software. This tendency can already be observed in Figure <xref ref-type="fig" rid="F9">9</xref>. The need for the sparse table arises from the collective communication scheme employed so far. The emission of an action potential by a neuron is communicated to all other compute nodes. These, in turn, decide with the help of the sparse table whether or not the sending neuron has a local target. Collective communication is a good implementation on the current petascale machines: Since there are around 2000 neurons simulated per core with 10,000 incoming synapses each, the probability that, assuming a random network, at least one spike must be communicated from one core to another is close to one. However, as each neuron sends its spike to all other <italic>MT</italic> cores, but only has targets on at most <italic>K</italic> of them, per neuron at least <italic>MT</italic> &#x02212; <italic>K</italic> spikes must be discarded on the other receiving cores.</p>
<p>Omitting the sparse table altogether requires directed communication to only those machines that harbor a target of the sending neuron. Two consequences arise for the required data structures. First, there needs to be a representation of the outgoing connections on the machine of the sending neuron, namely for each neuron one must know the nodes on which the neuron has targets, as already described in Morrison et al. (<xref ref-type="bibr" rid="B35">2005b</xref>). Second, the receiving machine needs a map from the id of the sending neuron to a list of its targets. Future work is required to find memory efficient implementations for these two data structures, algorithms to instantiate them during network setup, and appropriate communication methods. Such a framework will still benefit from the adaptive data structures presented in the current work for representing the actual synapses.</p>
<p>The simulation technology presented in this manuscript enables the scientific community to make full use of petascale supercomputers for neuroscientific research.</p>
<sec>
<title>Conflict of interest statement</title>
<p>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.</p></sec>
</sec>
</body>
<back>
<ack>
<p>We gratefully acknowledge Ryutaro Himeno for advising us from the start on the research programs developing and using the K computer, Mitsuhisa Sato for hosting our activities at RIKEN AICS in the later phase of the project, and Jakob Jordan for help in collecting the simulation data. Computing time on the K computer was provided through early access in the framework of the co-development program, project hp130120 of the General Use Category (2013), the Strategic Program (Neural Computation Unit, OIST), and MEXT SPIRE Supercomputational Life Science. Use of the JUGENE and JUQUEEN supercomputers in J&#x000FC;lich was made possible by VSR computation time grant JINB33. Partly supported by the Helmholtz Alliance on Systems Biology, the Initiative and Networking Fund of the Helmholtz Association, the Helmholtz young investigator group VH-NG-1028, the Next-Generation Supercomputer Project of MEXT, EU Grant 269921 (BrainScaleS), EU Grant 604102 (Human Brain Project, HBP), Research Council of Norway Grant 178892/V30 (eNeuro) and access to NOTUR supercomputing facilities. All network simulations carried out with NEST (<ext-link ext-link-type="uri" xlink:href="http://www.nest-simulator.org">http://www.nest-simulator.org</ext-link>).</p>
</ack>
<ref-list>
<title>References</title>
<ref id="B1">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Ajima</surname> <given-names>Y.</given-names></name> <name><surname>Sumimoto</surname> <given-names>S.</given-names></name> <name><surname>Shimizu</surname> <given-names>T.</given-names></name></person-group> (<year>2009</year>). <article-title>Tofu: a 6d mesh/torus interconnect for exascale computers</article-title>. <source>Computer</source> <volume>42</volume>, <fpage>36</fpage>&#x02013;<lpage>40</lpage>. <pub-id pub-id-type="doi">10.1109/MC.2009.370</pub-id></citation>
</ref>
<ref id="B2">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Alexandrescu</surname> <given-names>A.</given-names></name></person-group> (<year>2001</year>). <source>Modern C&#x0002B;&#x0002B; Design: Generic Programming and Design Patterns Applied. C&#x0002B;&#x0002B; In-Depth Series</source>. <publisher-loc>Boston, MA</publisher-loc>: <publisher-name>Addison-Wesley Professional</publisher-name>.</citation>
</ref>
<ref id="B3">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Ananthanarayanan</surname> <given-names>R.</given-names></name> <name><surname>Esser</surname> <given-names>S. K.</given-names></name> <name><surname>Simon</surname> <given-names>H. D.</given-names></name> <name><surname>Modha</surname> <given-names>D. S.</given-names></name></person-group> (<year>2009</year>). <article-title>The cat is out of the bag: cortical simulations with 10<sup>9</sup> neurons and 10<sup>13</sup> synapses</article-title>, in <source>Supercomputing 09: Proceedings of the ACM/IEEE SC2009 Conference on High Performance Networking and Computing</source> (<publisher-loc>Portland, OR</publisher-loc>).</citation>
</ref>
<ref id="B4">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Ananthanarayanan</surname> <given-names>R.</given-names></name> <name><surname>Modha</surname> <given-names>D. S.</given-names></name></person-group> (<year>2007</year>). <article-title>Anatomy of a cortical simulator</article-title>, in <source>Supercomputing 2007: Proceedings of the ACM/IEEE SC2007 Conference on High Performance Networking and Computing</source> (<publisher-loc>New York, NY</publisher-loc>: <publisher-name>Association for Computing Machinery</publisher-name>).</citation>
</ref>
<ref id="B5">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Bednar</surname> <given-names>J. A.</given-names></name></person-group> (<year>2009</year>). <article-title>Topographica: building and analyzing map-level simulations from Python, C/C&#x0002B;&#x0002B;, MATLAB, NEST, or NEURON components</article-title>. <source>Front. Neuroinform</source>. <volume>3</volume>, <fpage>8</fpage>. <pub-id pub-id-type="doi">10.3389/neuro.11.008.2009</pub-id></citation>
</ref>
<ref id="B6">
<citation citation-type="web"><person-group person-group-type="author"><name><surname>Board</surname> <given-names>O. A. R.</given-names></name></person-group> (<year>2008</year>). <source>OpenMP Application Program Interface. Specification</source>. Available online at: <ext-link ext-link-type="uri" xlink:href="http://www.openmp.org/mp-documents/spec30.pdf">http://www.openmp.org/mp-documents/spec30.pdf</ext-link></citation>
</ref>
<ref id="B7">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Brunel</surname> <given-names>N.</given-names></name></person-group> (<year>2000</year>). <article-title>Dynamics of sparsely connected networks of excitatory and inhibitory spiking neurons</article-title>. <source>J. Comput. Neurosci</source>. <volume>8</volume>, <fpage>183</fpage>&#x02013;<lpage>208</lpage>. <pub-id pub-id-type="doi">10.1023/A:1008925309027</pub-id><pub-id pub-id-type="pmid">10809012</pub-id></citation>
</ref>
<ref id="B8">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Corless</surname> <given-names>R. M.</given-names></name> <name><surname>Gonnet</surname> <given-names>G. H.</given-names></name> <name><surname>Hare</surname> <given-names>D. E. G.</given-names></name> <name><surname>Jeffrey</surname> <given-names>D. J.</given-names></name> <name><surname>Knuth</surname> <given-names>D. E.</given-names></name></person-group> (<year>1996</year>). <article-title>On the Lambert W function</article-title>. <source>Adv. Comput. Math</source>. <volume>5</volume>, <fpage>329</fpage>&#x02013;<lpage>359</lpage>. <pub-id pub-id-type="doi">10.1007/BF02124750</pub-id></citation>
</ref>
<ref id="B9">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Davison</surname> <given-names>A.</given-names></name> <name><surname>Br&#x000FC;derle</surname> <given-names>D.</given-names></name> <name><surname>Eppler</surname> <given-names>J.</given-names></name> <name><surname>Kremkow</surname> <given-names>J.</given-names></name> <name><surname>Muller</surname> <given-names>E.</given-names></name> <name><surname>Pecevski</surname> <given-names>D.</given-names></name> <etal/></person-group>. (<year>2008</year>). <article-title>PyNN: a common interface for neuronal network simulators</article-title>. <source>Front. Neuroinform</source>. <volume>2</volume>:<issue>11</issue>. <pub-id pub-id-type="doi">10.3389/neuro.11.011.2008</pub-id><pub-id pub-id-type="pmid">19194529</pub-id></citation>
</ref>
<ref id="B10">
<citation citation-type="web"><person-group person-group-type="author"><name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2012</year>). <article-title>Brain-scale neuronal network simulations on K</article-title>, in <source>Proceedings of the 4th Biosupercomputing Sympoisum</source> (<publisher-loc>Tokyo</publisher-loc>: Tokyo International Forum (Hall D7): Next-Generation Integrated Simulation of Living Matter (ISLiM) program of MEXT), <fpage>83</fpage>&#x02013;<lpage>85</lpage>. Available online at: <ext-link ext-link-type="uri" xlink:href="http://www.csrp.riken.jp/4thbscs/4th-BSCS-proceedings.pdf">http://www.csrp.riken.jp/4thbscs/4th-BSCS-proceedings.pdf</ext-link></citation>
</ref>
<ref id="B11">
<citation citation-type="web"><person-group person-group-type="author"><name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2013</year>). <article-title>The road to brain-scale simulations on K</article-title>. <source>Biosupercomput. Newslett</source>. <volume>8</volume>, <fpage>8</fpage>. Available online at: <ext-link ext-link-type="uri" xlink:href="http://www.csrp.riken.jp">http://www.csrp.riken.jp</ext-link></citation>
</ref>
<ref id="B12">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Eppler</surname> <given-names>J.</given-names></name> <name><surname>Plesser</surname> <given-names>H.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name> <name><surname>Gewaltig</surname> <given-names>M.-O.</given-names></name></person-group> (<year>2007</year>). <article-title>Multithreaded and distributed simulation of large biological neuronal networks</article-title>, in <source>Proceedings of European PVM/MPI, Paris</source>, <volume>Vol. 4757</volume> (<publisher-loc>Paris</publisher-loc>: <publisher-name>Springer LNCS</publisher-name>), <fpage>391</fpage>&#x02013;<lpage>392</lpage>.</citation>
</ref>
<ref id="B13">
<citation citation-type="other"><person-group person-group-type="author"><name><surname>Eppler</surname> <given-names>J. M.</given-names></name></person-group> (<year>2006</year>). <source>A Multithreaded and Distributed System for The Simulation of Large Biological Neural Networks</source>. Master&#x00027;s thesis, Albert Ludwig University Freiburg.</citation>
</ref>
<ref id="B14">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Eppler</surname> <given-names>J. M.</given-names></name> <name><surname>Helias</surname> <given-names>M.</given-names></name> <name><surname>Muller</surname> <given-names>E.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name> <name><surname>Gewaltig</surname> <given-names>M.</given-names></name></person-group> (<year>2009</year>). <article-title>PyNEST: a convenient interface to the NEST simulator</article-title>. <source>Front. Neuroinform</source>. <volume>2</volume>:<issue>12</issue>. <pub-id pub-id-type="doi">10.3389/neuro.11.012.2008</pub-id><pub-id pub-id-type="pmid">19198667</pub-id></citation>
</ref>
<ref id="B15">
<citation citation-type="book"><person-group person-group-type="author"><collab>Free Software Foundation</collab></person-group> (<year>2013</year>). <source>GCC 4.8.2 Manual</source>. <publisher-loc>Boston, MA</publisher-loc>: <publisher-name>Free Software Foundation</publisher-name>.</citation>
</ref>
<ref id="B16">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Gamma</surname> <given-names>E.</given-names></name> <name><surname>Helm</surname> <given-names>R.</given-names></name> <name><surname>Johnson</surname> <given-names>R.</given-names></name> <name><surname>Vlissides</surname> <given-names>J.</given-names></name></person-group> (<year>1994</year>). <source>Design Patterns: Elements of Reusable Object-Oriented Software. Professional Computing Series</source>. <publisher-loc>Boston, MA</publisher-loc>: <publisher-name>Addison-Wesely</publisher-name>.</citation>
</ref>
<ref id="B17">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Gewaltig</surname> <given-names>M.-O.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2007</year>). <article-title>NEST (NEural Simulation Tool)</article-title>. <source>Scholarpedia</source> <volume>2</volume>, <fpage>1430</fpage>. <pub-id pub-id-type="doi">10.4249/scholarpedia.1430</pub-id></citation>
</ref>
<ref id="B18">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Goodman</surname> <given-names>D.</given-names></name> <name><surname>Brette</surname> <given-names>R.</given-names></name></person-group> (<year>2013</year>). <article-title>Brian simulator</article-title>. <source>Scholarpedia</source> <volume>8</volume>, <fpage>10883</fpage>. <pub-id pub-id-type="doi">10.4249/scholarpedia.10883</pub-id></citation>
</ref>
<ref id="B19">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Hanuschkin</surname> <given-names>A.</given-names></name> <name><surname>Kunkel</surname> <given-names>S.</given-names></name> <name><surname>Helias</surname> <given-names>M.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2010</year>). <article-title>A general and efficient method for incorporating precise spike times in globally time-driven simulations</article-title>. <source>Front. Neuroinform</source>. <volume>4</volume>:<issue>113</issue>. <pub-id pub-id-type="doi">10.3389/fninf.2010.00113</pub-id><pub-id pub-id-type="pmid">21031031</pub-id></citation>
</ref>
<ref id="B20">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Hardy</surname> <given-names>G.</given-names></name> <name><surname>Wright</surname> <given-names>E.</given-names></name></person-group> (<year>1975</year>). <source>An Introduction to the Theory of Numbers</source>. <publisher-loc>London</publisher-loc>: <publisher-name>Oxford at the Clarendon Press</publisher-name>.</citation>
</ref>
<ref id="B21">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Helias</surname> <given-names>M.</given-names></name> <name><surname>Kunkel</surname> <given-names>S.</given-names></name> <name><surname>Masumoto</surname> <given-names>G.</given-names></name> <name><surname>Igarashi</surname> <given-names>J.</given-names></name> <name><surname>Eppler</surname> <given-names>J. M.</given-names></name> <name><surname>Ishii</surname> <given-names>S.</given-names></name> <etal/></person-group>. (<year>2012</year>). <article-title>Supercomputers ready for use as discovery machines for neuroscience</article-title>. <source>Front. Neuroinform</source>. <volume>6</volume>:<issue>26</issue>. <pub-id pub-id-type="doi">10.3389/fninf.2012.00026</pub-id><pub-id pub-id-type="pmid">23129998</pub-id></citation>
</ref>
<ref id="B22">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Hines</surname> <given-names>M.</given-names></name> <name><surname>Davison</surname> <given-names>A. P.</given-names></name> <name><surname>Muller</surname> <given-names>E.</given-names></name></person-group> (<year>2009</year>). <article-title>NEURON and Python</article-title>. <source>Front. Neuroinform</source>. <volume>3</volume>:<issue>1</issue>. <pub-id pub-id-type="doi">10.3389/neuro.11.001.2009</pub-id><pub-id pub-id-type="pmid">19198661</pub-id></citation>
</ref>
<ref id="B23">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Hines</surname> <given-names>M.</given-names></name> <name><surname>Kumar</surname> <given-names>S.</given-names></name> <name><surname>Sch&#x000FC;rmann</surname> <given-names>F.</given-names></name></person-group> (<year>2011</year>). <article-title>Comparison of neuronal spike exchange methods on a Blue Gene/P supercomputer</article-title>. <source>Front. Comput. Neurosci</source>. <volume>5</volume>:<issue>49</issue>. <pub-id pub-id-type="doi">10.3389/fncom.2011.00049</pub-id><pub-id pub-id-type="pmid">22121345</pub-id></citation>
</ref>
<ref id="B24">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Hines</surname> <given-names>M. L.</given-names></name> <name><surname>Markram</surname> <given-names>H.</given-names></name> <name><surname>Sch&#x000FC;rmann</surname> <given-names>F.</given-names></name></person-group> (<year>2008</year>). <article-title>Fully implicit parallel simulation of single neurons</article-title>. <source>J. Comput. Neurosci</source>. <volume>25</volume>, <fpage>439</fpage>&#x02013;<lpage>448</lpage>. <pub-id pub-id-type="doi">10.1007/s10827-008-0087-5</pub-id><pub-id pub-id-type="pmid">18379867</pub-id></citation>
</ref>
<ref id="B25">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Jeffreys</surname> <given-names>H.</given-names></name> <name><surname>Jeffreys</surname> <given-names>B.</given-names></name></person-group> (<year>1956</year>). <source>Methods of Mathematical Physics</source>. <publisher-loc>Cambridge</publisher-loc>: <publisher-name>Cambridge University Press</publisher-name>.</citation>
</ref>
<ref id="B26">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Knuth</surname> <given-names>D. E.</given-names></name></person-group> (<year>1997</year>). <source>The Art of Computer Programming: Fundamental Algorithms</source> <edition>(3rd Edn.)</edition>, <volume>Vol. 1.</volume> <publisher-loc>Boston, MA</publisher-loc>: <publisher-name>Addison Wesley</publisher-name>.</citation>
</ref>
<ref id="B27">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Kozloski</surname> <given-names>J.</given-names></name> <name><surname>Wagner</surname> <given-names>J.</given-names></name></person-group> (<year>2011</year>). <article-title>An ultrascalable solution to large-scale neural tissue simulation</article-title>. <source>Front. Neuroinform</source>. <volume>5</volume>:<issue>15</issue>. <pub-id pub-id-type="doi">10.3389/fninf.2011.00015</pub-id><pub-id pub-id-type="pmid">21954383</pub-id></citation>
</ref>
<ref id="B28">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Kunkel</surname> <given-names>S.</given-names></name> <name><surname>Helias</surname> <given-names>M.</given-names></name> <name><surname>Potjans</surname> <given-names>T. C.</given-names></name> <name><surname>Eppler</surname> <given-names>J. M.</given-names></name> <name><surname>Plesser</surname> <given-names>H. E.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name> <etal/></person-group>. (<year>2012a</year>). <article-title>Memory consumption of neuronal network simulators at the brain scale</article-title>, in <source>NIC Symposium 2012 Proceedings, Volume 45 of NIC Series</source>, eds <person-group person-group-type="editor"><name><surname>Binder</surname> <given-names>K.</given-names></name> <name><surname>M&#x000FC;nster</surname> <given-names>G.</given-names></name> <name><surname>Kremer</surname> <given-names>M.</given-names></name></person-group> (<publisher-loc>J&#x000FC;lich</publisher-loc>: <publisher-name>Forschungszentrum J&#x000FC;lich GmbH</publisher-name>), <fpage>81</fpage>&#x02013;<lpage>88</lpage>. <pub-id pub-id-type="pmid">22291636</pub-id></citation>
</ref>
<ref id="B29">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Kunkel</surname> <given-names>S.</given-names></name> <name><surname>Potjans</surname> <given-names>T. C.</given-names></name> <name><surname>Eppler</surname> <given-names>J. M.</given-names></name> <name><surname>Plesser</surname> <given-names>H. E.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2012b</year>). <article-title>Meeting the memory challenges of brain-scale simulation</article-title>. <source>Front. Neuroinform</source>. <volume>5</volume>:<issue>35</issue>. <pub-id pub-id-type="doi">10.3389/fninf.2011.00035</pub-id><pub-id pub-id-type="pmid">22291636</pub-id></citation>
</ref>
<ref id="B30">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Kunkel</surname> <given-names>S.</given-names></name> <name><surname>Schmidt</surname> <given-names>M.</given-names></name> <name><surname>Eppler</surname> <given-names>J.</given-names></name> <name><surname>Plesser</surname> <given-names>H.</given-names></name> <name><surname>Igarashi</surname> <given-names>J.</given-names></name> <name><surname>Masumoto</surname> <given-names>G.</given-names></name> <etal/></person-group>. (<year>2013</year>). <article-title>From laptops to supercomputers: a single highly scalable code base for spiking neuronal network simulations</article-title>. <source>BMC Neurosci</source>. <volume>14</volume>(<supplement>Suppl. 1</supplement>):<fpage>P163</fpage>. <pub-id pub-id-type="doi">10.1186/1471-2202-14-S1-P163</pub-id></citation>
</ref>
<ref id="B31">
<citation citation-type="web"><person-group person-group-type="author"><collab>Message Passing Interface Forum.</collab></person-group> (<year>1994</year>). <source>MPI: A Message-Passing Interface Standard</source>. Technical Report UT-CS-94-230. <publisher-loc>Knoxville, TN</publisher-loc>: <publisher-name>University of Tennessee</publisher-name>. <ext-link ext-link-type="uri" xlink:href="http://www.mpi-forum.org/docs/mpi-10.ps">http://www.mpi-forum.org/docs/mpi-10.ps</ext-link></citation>
</ref>
<ref id="B32">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Aertsen</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2007</year>). <article-title>Spike-timing dependent plasticity in balanced random networks</article-title>. <source>Neural Comput</source>. <volume>19</volume>, <fpage>1437</fpage>&#x02013;<lpage>1467</lpage>. <pub-id pub-id-type="doi">10.1162/neco.2007.19.6.1437</pub-id><pub-id pub-id-type="pmid">17444756</pub-id></citation>
</ref>
<ref id="B33">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name> <name><surname>Gerstner</surname> <given-names>W.</given-names></name></person-group> (<year>2008</year>). <article-title>Phenomenological models of synaptic plasticity based on spike-timing</article-title>. <source>Biol. Cybernet</source>. <volume>98</volume>, <fpage>459</fpage>&#x02013;<lpage>478</lpage>. <pub-id pub-id-type="doi">10.1007/s00422-008-0233-1</pub-id><pub-id pub-id-type="pmid">18491160</pub-id></citation>
</ref>
<ref id="B34">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Hake</surname> <given-names>J.</given-names></name> <name><surname>Straube</surname> <given-names>S.</given-names></name> <name><surname>Plesser</surname> <given-names>H. E.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2005a</year>). <article-title>Precise spike timing with exact subthreshold integration in discrete time network simulations</article-title>, in <source>Proceedings of the 30th G&#x000F6;ttingen Neurobiology Conference</source>, <volume>205B</volume> (<publisher-loc>G&#x000F6;ttingen</publisher-loc>).</citation>
</ref>
<ref id="B35">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Mehring</surname> <given-names>C.</given-names></name> <name><surname>Geisel</surname> <given-names>T.</given-names></name> <name><surname>Aertsen</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2005b</year>). <article-title>Advancing the boundaries of high connectivity network simulation with distributed computing</article-title>. <source>Neural Comput</source>. <volume>17</volume>, <fpage>1776</fpage>&#x02013;<lpage>1801</lpage>. <pub-id pub-id-type="doi">10.1162/0899766054026648</pub-id><pub-id pub-id-type="pmid">15969917</pub-id></citation>
</ref>
<ref id="B36">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Plesser</surname> <given-names>H. E.</given-names></name> <name><surname>Eppler</surname> <given-names>J. M.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name> <name><surname>Gewaltig</surname> <given-names>M.-O.</given-names></name></person-group> (<year>2007</year>). <article-title>Efficient parallel simulation of large-scale neuronal networks on clusters of multiprocessor computers</article-title>, in <source>Euro-Par 2007: Parallel Processing, Volume 4641 of Lecture Notes in Computer Science</source>, eds <person-group person-group-type="editor"><name><surname>Kermarrec</surname> <given-names>A.-M.</given-names></name> <name><surname>Boug&#x000E9;</surname> <given-names>L.</given-names></name> <name><surname>Priol</surname> <given-names>T.</given-names></name></person-group> (<publisher-loc>Berlin</publisher-loc>: <publisher-name>Springer-Verlag</publisher-name>), <fpage>672</fpage>&#x02013;<lpage>681</lpage>.</citation>
</ref>
<ref id="B37">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Potjans</surname> <given-names>W.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2010</year>). <article-title>Enabling functional neural circuit simulations with distributed computing of neuromodulated plasticity</article-title>. <source>Front. Comput. Neurosci</source>. <volume>4</volume>:<issue>141</issue>. <pub-id pub-id-type="doi">10.3389/fncom.2010.00141</pub-id><pub-id pub-id-type="pmid">21151370</pub-id></citation>
</ref>
<ref id="B38">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Rentzsch</surname> <given-names>J.</given-names></name></person-group> (<year>2005</year>). <source>Data Alignment: Straighten up And Fly Right</source>. <publisher-name>IBM developerWorks</publisher-name>.</citation>
</ref>
<ref id="B39">
<citation citation-type="book"><person-group person-group-type="author"><collab>RIKEN BSI.</collab></person-group> (<year>2013</year>). <source>Largest Neuronal Network Simulation Achieved Using K Computer</source>. <publisher-loc>Wako</publisher-loc>: <publisher-name>Press Release</publisher-name>.</citation>
</ref>
<ref id="B40">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Rotter</surname> <given-names>S.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>1999</year>). <article-title>Exact digital simulation of time-invariant linear systems with applications to neuronal modeling</article-title>. <source>Biol. Cybernet</source>. <volume>81</volume>, <fpage>381</fpage>&#x02013;<lpage>402</lpage>. <pub-id pub-id-type="doi">10.1007/s004220050570</pub-id><pub-id pub-id-type="pmid">10592015</pub-id></citation>
</ref>
<ref id="B41">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Stroustrup</surname> <given-names>B.</given-names></name></person-group> (<year>1997</year>). <source>The C&#x0002B;&#x0002B; Programming Language, 3 Edn</source>. <publisher-loc>New York, NY</publisher-loc>: <publisher-name>Addison-Wesely</publisher-name>.</citation>
</ref>
<ref id="B42">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Tsodyks</surname> <given-names>M.</given-names></name> <name><surname>Pawelzik</surname> <given-names>K.</given-names></name> <name><surname>Markram</surname> <given-names>H.</given-names></name></person-group> (<year>1998</year>). <article-title>Neural networks with dynamic synapses</article-title>. <source>Neural Comput</source>. <volume>10</volume>, <fpage>821</fpage>&#x02013;<lpage>835</lpage>. <pub-id pub-id-type="doi">10.1162/089976698300017502</pub-id><pub-id pub-id-type="pmid">9573407</pub-id></citation>
</ref>
<ref id="B43">
<citation citation-type="web"><person-group person-group-type="author"><name><surname>Tsodyks</surname> <given-names>M.</given-names></name> <name><surname>Uziel</surname> <given-names>A.</given-names></name> <name><surname>Markram</surname> <given-names>H.</given-names></name></person-group> (<year>2000</year>). <article-title>Synchrony generation in recurrent networks with frequency-dependent synapses</article-title>. <source>J. Neurosci</source>. <volume>20</volume>, <fpage>RC50</fpage>. Available online at: <ext-link ext-link-type="uri" xlink:href="http://www.jneurosci.org/content/20/1/RC50.short">http://www.jneurosci.org/content/20/1/RC50.short</ext-link> <pub-id pub-id-type="pmid">10627627</pub-id></citation>
</ref>
<ref id="B44">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>van Albada</surname> <given-names>S. J.</given-names></name> <name><surname>Kunkel</surname> <given-names>S.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name> <name><surname>Diesmann</surname> <given-names>M.</given-names></name></person-group> (<year>2014</year>). <article-title>Integrating brain structure and dynamics on supercomputers</article-title>, in <source>Proceedings of Braincomp July 8-11 2013: workshop on Brain-Inspired Computing</source>, eds <person-group person-group-type="editor"><name><surname>Grandinetti</surname> <given-names>L.</given-names></name> <name><surname>Lippert</surname> <given-names>T.</given-names></name> <name><surname>Petkov</surname> <given-names>N.</given-names></name></person-group> (<publisher-loc>Cetraro</publisher-loc>: <publisher-name>Springer</publisher-name>).</citation>
</ref>
<ref id="B45">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Vandervoorde</surname> <given-names>D.</given-names></name> <name><surname>Josuttis</surname> <given-names>N.</given-names></name></person-group> (<year>2003</year>). <source>Templates: The Complete Guide, 1st Edn</source>. <publisher-loc>Boston, MA</publisher-loc>: <publisher-name>Addison Wesley</publisher-name>.</citation>
</ref>
<ref id="B46">
<citation citation-type="book"><person-group person-group-type="author"><name><surname>Yonezawa</surname> <given-names>A.</given-names></name> <name><surname>Watanabe</surname> <given-names>T.</given-names></name> <name><surname>Yokokawa</surname> <given-names>M.</given-names></name> <name><surname>Sato</surname> <given-names>M.</given-names></name> <name><surname>Hirao</surname> <given-names>K.</given-names></name></person-group> (<year>2011</year>). <article-title>Advanced institute for computational science (aics): Japanese national high-performance computing research institute and its 10-petaflops supercomputer &#x0201C;K&#x0201D;</article-title>, in <source>State of the Practice Reports</source>, SC &#x02032;11 (<publisher-loc>New York, NY</publisher-loc>: <publisher-name>ACM</publisher-name>), <fpage>13:1</fpage>&#x02013;<lpage>13:8</lpage>.</citation>
</ref>
<ref id="B47">
<citation citation-type="journal"><person-group person-group-type="author"><name><surname>Zaytsev</surname> <given-names>Y. V.</given-names></name> <name><surname>Morrison</surname> <given-names>A.</given-names></name></person-group> (<year>2014</year>). <article-title>CyNEST: a maintainable Cython-based interface for the NEST simulator</article-title>. <source>Front. Neuroinform</source>. <volume>8</volume>:<issue>23</issue>. <pub-id pub-id-type="doi">10.3389/fninf.2014.00023</pub-id><pub-id pub-id-type="pmid">24672470</pub-id></citation>
</ref>
</ref-list>
<app-group>
<app id="A1">
<title>Appendix</title>
<table-wrap position="float">
<label>Algorithm 1</label>
<caption><p><bold>Recursive definition of homogeneous containers implementing low-overhead vector for small numbers of elements</bold>. The <italic>K</italic> data elements (synapse objects) of template type <monospace>connectionT</monospace> are stored in a C-style array (line 10) and hence consume <italic>K</italic> &#x000B7; sizeof(connectionT) Bytes. 8 B of additional overhead are due to the virtual function table (vtable) pointer inherited from the abstract base class <monospace>ConnectorBase</monospace> (see also Figure <xref ref-type="fig" rid="F4">4</xref>). The first template definition (line 6) serves as the recursion continuation step with the number <italic>K</italic> of stored elements as the recurrence parameter. The method <monospace>push_back</monospace> is inherited from the abstract base class <monospace>vector_like</monospace>. For template arguments <italic>K</italic> &#x0003C; <italic>K</italic><sub>cutoff</sub> push_back recursively instantiates a container of size <italic>K</italic> &#x0002B; 1 (line 27) at compile time, joins the previously stored contents with the new element and destroys itself (line 31). The new container is returned to the caller and is stored in the adaptive data structure (see also Algorithm 2). Recursion termination is achieved by providing a template specialization for <italic>K</italic><sub>cutoff</sub> (line 39), which uses a <monospace>std::vector&#x0003C;connectionT&#x0003E;</monospace> to store the data elements.<monospace><italic>K</italic><sub>cutoff</sub> &#x0003D; 3</monospace> is a preprocessor define fixed at compile time.</p></caption>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;1 // <italic>inheritance from abstract base class</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;2 // <italic>1. vector_like: to provide push_back as common interface</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;3 // <italic>2. ConnectorBase: to have common base class type for all containers</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;4</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;5 // <italic>recursion step</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;6 <bold>template</bold>&#x0003C;<bold>int</bold> K, <bold>typename</bold> connectionT&#x0003E;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;7 <bold>struct</bold> HomConnector</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;8&#x000A0;&#x000A0;&#x000A0;: <bold>public</bold> vector_like &#x0003C;connectionT&#x0003E;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;9 {</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>10&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;connectionT C_[K];</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>11</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>12&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;HomConnector(<bold>const</bold> HomConnector&#x0003C;K&#x02212;1, connectionT&#x0003E; &#x00026; Cm1,</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>13&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0; <bold>const</bold> connectionT &#x00026; c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>14&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;: syn_id_(Cm1.syn_id_)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>15&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;{</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>16&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>for</bold> (<bold>int</bold> i&#x0003D;0; i&#x0003C;K&#x02212;1; i&#x0002B;&#x0002B;)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>17&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;C_[i] &#x0003D; Cm1.C_[i];</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>18&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;C_[K&#x02212;1] &#x0003D; c;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>19&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;}</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>20</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>21&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>pushback: recursion step to next container</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>22&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;HomConnector &#x0003C;K&#x0002B;1, connectionT&#x0003E; &#x00026; push_back(<bold>const</bold> connectionT &#x00026; c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>23&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;{</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>24&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;//<italic>pass on contents and</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>25&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>recursively instantiate container for K&#x0002B;1 elements</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>26&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>use pool allocator, pass (&#x0002A;this, c) as args to constructor</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>27&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;HomConnector&#x0003C;K&#x0002B;1, connectionT&#x0003E; &#x0002A;newconn &#x0003D;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>28&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;allocate &#x0003C;HomConnector&#x0003C;K&#x0002B;1, ConnectionT&#x0003E; &#x0003E;(&#x0002A;<bold>this</bold>, c);</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>29</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>30&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0; // <italic>delete this instance</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>31&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>delete this</bold>;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>32</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>33&#x000A0;&#x000A0;&#x000A0; // <italic>return container of next size</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>34&#x000A0;&#x000A0;&#x000A0; <bold>return</bold> &#x0002A;newconn;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>35&#x000A0;&#x000A0;&#x000A0;&#x000A0;}</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>36 };</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>37</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>38 // <italic>recursion termination by template specialization</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>39 <bold>template</bold>&#x0003C;<bold>typename</bold> connectionT&#x0003E;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>40 <bold>struct</bold> HomConnector&#x0003C;K_cutoff, connectionT&#x0003E;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>41&#x000A0;&#x000A0;&#x000A0;: <bold>public</bold> vector_like &#x0003C;connectionT&#x0003E;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>42 {</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>43&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;std::vector&#x0003C;connectionT&#x0003E; C_;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>44&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x02026;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>45&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;HomConnector&#x0003C;K_cutoff, connectionT&#x0003E; &#x00026; push_back(<bold>const</bold> connectionT &#x00026; c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>46&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;{</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>47&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;C_.push_back(c);</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>48&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>return</bold> &#x0002A;<bold>this</bold>;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>49&#x000A0;&#x000A0;&#x000A0;}</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>50 };</monospace></td>
</tr>
</tbody>
</table>
</table-wrap>
<table-wrap position="float">
<label>Algorithm 2</label>
<caption><p><bold>Algorithm to create new connections using adaptive data containers</bold>. The algorithm is implemented in the method <monospace>add_connection()</monospace> of <monospace>ConnectorModel</monospace> (see also Figure <xref ref-type="fig" rid="F4">4</xref>). The template function <monospace>allocate&#x0003C;T&#x0003E;</monospace> invokes the custom pool allocator, providing for each thread memory from a separate pool. The different branches of the algorithm marked by Case 0-3 refer to the description in the main text.</p></caption>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;1 ConnectorBase&#x0002A; add_connection(Node&#x00026; tgt, Node&#x00026; src,</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;2&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;ConnectorBase&#x0002A; conn, ConnectionT&#x00026; c):</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;3</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;4&#x000A0;&#x000A0;// <italic>source neuron does not yet have any local targets (Case 0)</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;5&#x000A0;&#x000A0;<bold>if</bold> conn &#x0003D;&#x0003D; 0:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;6&#x000A0;&#x000A0;&#x000A0;&#x000A0;c.check_connection(t_lastspike)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;7&#x000A0;&#x000A0;&#x000A0;&#x000A0;conn &#x02190; allocate &#x0003C;HomConnector&#x0003C;1, ConnectionT&#x0003E; &#x0003E;(c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;8</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;9&#x000A0;&#x000A0;// <italic>source neuron has at least one local target</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>10 &#x000A0;<bold>else</bold>:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>11&#x000A0;&#x000A0;&#x000A0;&#x000A0;c.check_connection(t_lastspike(&#x0002A;conn).get_t_lastspike())</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>12</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>13&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>all existing local synapses are of the same type (Case 1,2)</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>14&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>if</bold> (&#x0002A;conn).homogeneous_model():</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>15</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>16&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>new synapse is of same type as existing ones (stay in Case 1,2)</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>17&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>if</bold> (&#x0002A;conn).get_syn_id() &#x0003D;&#x0003D; c.get_syn_id():</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>18&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;hom_vec &#x02190; <bold>static_cast</bold>&#x0003C;vector_like&#x0003C;ConnectionT&#x0003E; &#x0002A; &#x0003E;(conn)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>19&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;conn &#x02190; &#x00026;(&#x0002A;hom_vec).push_back(c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>20</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>21 &#x000A0;&#x000A0;// <italic>new synapse is of different type than existing ones (switch to Case 3)</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>22 &#x000A0;&#x000A0;<bold>else</bold>:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>23 &#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;het_conn &#x02190; allocate &#x0003C;HetConnector&#x0003E;()</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>24 &#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;(&#x0002A;het_conn).push_back(conn)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>25 &#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;hom_conn &#x02190; allocate&#x0003C;HomConnector&#x0003C;1, ConnectionT&#x0003E; &#x0003E;(c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>26 &#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;(&#x0002A;het_conn).push_back(hom_conn)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>27 &#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;conn &#x02190; het_conn</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>28</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>29 &#x000A0;// <italic>different types of local synapses exist (Case 3)</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>30 &#x000A0;<bold>else</bold>:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>31 &#x000A0;&#x000A0;&#x000A0;het_conn &#x02190; <bold>static_cast</bold> &#x0003C;HetConnector&#x0003E;(conn)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>32 &#x000A0;&#x000A0;&#x000A0;<bold>foreach</bold> h_conn <bold>in</bold> het_conn:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>33</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>34 &#x000A0;&#x000A0;// <italic>at least one local synapse of this type already exists</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>35 &#x000A0;&#x000A0;<bold>if</bold> (&#x0002A;h_conn).get_syn_id() &#x0003D;&#x0003D; c.get_syn_id():</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>36 &#x000A0;&#x000A0;&#x000A0;&#x000A0;hom_conn &#x02190; <bold>static_cast</bold>&#x0003C;HomConnector&#x0003C;ConnectionT&#x0003E; &#x0002A; &#x0003E;(h_conn)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>37 &#x000A0;&#x000A0;&#x000A0;&#x000A0;hom_conn &#x02190; &#x00026;(&#x0002A;hom_conn).push_back(c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>38 &#x000A0;&#x000A0;&#x000A0;&#x000A0;found &#x02190; <bold>true</bold></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>39 &#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>break</bold></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>40</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>41 &#x000A0;&#x000A0;// <italic>local synapses of this type do not yet exist</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>42 &#x000A0;&#x000A0;<bold>if not</bold> found:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>43 &#x000A0;&#x000A0;&#x000A0;&#x000A0;hom_conn &#x02190; allocate &#x0003C;HomConnector&#x0003C;1, ConnectionT&#x0003E; &#x0003E;(c)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>44 &#x000A0;&#x000A0;&#x000A0;&#x000A0;(&#x0002A;het_conn).push_back(hom_conn)</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>45</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>46 &#x000A0;<bold>return</bold> conn</monospace></td>
</tr>
</tbody>
</table>
</table-wrap>
<table-wrap position="float">
<label>Algorithm 3</label>
<caption><p><monospace>get_node_by_gid()</monospace> in <monospace>SparseNodeArray</monospace> returns <monospace>0</monospace> for non-local nodes and the pointer to the requested node for local nodes.</p></caption>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;1 Node&#x0002A; get_node_by_gid(index gid):</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;2</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;3&#x000A0;&#x000A0;&#x000A0;<bold>if</bold> gid &#x0003E; max_gid:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;4&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>throw</bold> UnknownNode()&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>node does not exist</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;5</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;6&#x000A0;&#x000A0;&#x000A0;<bold>if</bold> gid &#x0003D;&#x0003D; 0:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;7&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>return</bold> local_nodes[0].node // <italic>root node</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;8&#x000A0;&#x000A0;&#x000A0;</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>&#x000A0;9&#x000A0;&#x000A0;&#x000A0;<bold>if not</bold> (local_min_gid &#x02264; gid &#x02264; local_max_gid):</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>10&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>return</bold> 0&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>node not local</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>11</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>12&#x000A0;&#x000A0;&#x000A0;// <italic>estimate location in sparse node array</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>13&#x000A0;&#x000A0;&#x000A0;idx &#x02190; floor(1 &#x0002B; alpha &#x0002A; (gid &#x02212; local_min_gid))</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>14</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>15&#x000A0;&#x000A0;&#x000A0;// <italic>search to the left</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>16&#x000A0;&#x000A0;&#x000A0;<bold>while</bold> 0 &#x0003C; idx <bold>and</bold> gid &#x0003C; local_nodes[idx].gid:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>17&#x000A0;&#x000A0;&#x000A0;&#x000A0;idx &#x02190; idx&#x02212;1</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>18</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>19&#x000A0;&#x000A0;&#x000A0;// <italic>search to the right</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>20&#x000A0;&#x000A0;&#x000A0;<bold>while</bold> idx &#x0003C; local_nodes.size <bold>and</bold> local_nodes[idx].gid &#x0003C; gid:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>21&#x000A0;&#x000A0;&#x000A0;&#x000A0;idx &#x02190; idx&#x0002B;1</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>22</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>23&#x000A0;&#x000A0;&#x000A0;<bold>if</bold> idx &#x0003C; local_nodes.size <bold>and</bold> local_nodes[idx].gid &#x0003D;&#x0003D; gid:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>24&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>return</bold> local_nodes[idx].node&#x000A0;&#x000A0;&#x000A0;// <italic>local node found</italic></monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>25&#x000A0;&#x000A0;&#x000A0;<bold>else</bold>:</monospace></td>
</tr>
<tr>
<td align="left" valign="top"><monospace>26&#x000A0;&#x000A0;&#x000A0;&#x000A0;<bold>return</bold> 0&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;&#x000A0;// <italic>node not local</italic></monospace></td>
</tr>
</tbody>
</table>
</table-wrap>
</app>
</app-group>
</back>
</article>
