Skip to content

Performance Improvements - #775

Merged
Hananel-Hazan merged 15 commits into
masterfrom
performance
Aug 4, 2026
Merged

Performance Improvements#775
Hananel-Hazan merged 15 commits into
masterfrom
performance

Conversation

@C-Earl

@C-Earl C-Earl commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Various performance improvements:

  • Fixed GPU variables being redefined at each timestep in MSTDP learning rule to prevent unnecessary CPU->GPU overhead
  • Added 'foldable' pipelines: Pipeline features are collapsed into as few matrix operations as possible during a compute step. Saves runtime, particularly for GPU implementations.
  • Added sparse/event-based computation. Previously during a compute step for a common feature like Weight, an operation like W @ v would be executed for a feature-matrix W and pre-synaptic spike vector v. If activity in v is sparse, many of the multiplication operations would result in 0's, wasting computational time. The event-based computation removes columns adjacent to entries in v which are 0's to prevent this.
  • Added test cases for the MultiCompartmentConnection and validity of all the above

Also added a new stress-test model at examples/stress_test/example_network.py and benchmark scripts at examples/benchmark/sparse_computation and foldable pipeline. The model contains 20,000 excitatory + 2,000 inhibitory neurons, and MSTDP learning rules with sparsely populated multicompartment connections.

The model itself produces no meaningful behavior, but tests a general case of a large model with medium-activity and sparse connections. Benchmark scripts tell us the performance improvements that come with this model:

Foldable pipelines + sparse compute (combined)
  (%-time speedup of 'fold_sparse' vs baseline 'expansion')
========================================================================
  [CPU ] exc=  2000  time= 20  |  expansion=    8.14 ms   fold_sparse=    3.61 ms   |  speedup =  +55.6% time
  [CUDA] exc= 20000  time= 50  |  expansion=  107.58 ms   fold_sparse=   23.09 ms   |  speedup =  +78.5% time

(Times reported are median run times per- compute call)

C-Earl and others added 13 commits July 24, 2026 09:34
- test_connections.py: drop __init__ constructors so pytest actually
  collects the classes (0 tests ran before); fix test_transfer to move
  the connection to the device before asserting placement; rename the
  argument-taking test_weights to check_weights (manual matrix check,
  driven from __main__); make the __main__ MCC list self-discovering.
- topology.py: densify sparse feature values ([1, src, tgt] from
  prime_feature) into [src, tgt] fold factors — Probability(sparse=True)
  crashed with "expand is unsupported for Sparse tensors"; restore
  empty-pipeline fan-in behavior; skip None factors from sub-features;
  gate sparse_compute per device (CUDA needs >= 4e6 synapses to amortize
  the nonzero() sync; on CPU the gather helps even at small sizes).
- topology_features.py: sub-features return None (identity in the fold)
  instead of int 1.
- MCC_learning.py: key the cached MSTDP decay factors on (dt, device) so
  changing network.dt or moving devices recomputes them instead of
  silently reusing stale values.
- _bench_common.py: repo root is three levels up, not two — the
  benchmarks were unimportable without an installed bindsnet.
- New regression tests: sparse Probability (dense + gather paths), empty
  pipeline, sub-feature identity, MSTDP dt change, CUDA gate equality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- topology.py: cache the folded (a_eff, b_sum) whenever every pipeline
  feature is static (is_static flag; Probability, MeanField, adaptation
  features, and sub-features are dynamic). Invalidated on learning
  updates, normalize, reset, and device/dtype moves. Weight*Mask*
  Intensity at 100x20000: 4506 -> 66 us/step CPU, 321 -> 58 us GPU.
- topology_features.py: Weight's per-time-step normalization is deferred
  by the connection until after the fold consumes the pre-norm value,
  removing the full-matrix clone every step. Fix the reduction assertion
  (isinstance(x, callable) raised TypeError for any custom reduction).
- MCC_learning.py: MSTDP with the default batch reductions applies the
  reward-weighted eligibility as two rank-1 addmm_ updates instead of
  materializing [batch, src, tgt] every step (5077 -> 899 us CPU,
  834 -> 332 us GPU at 100x20000; exact same numbers, verified to 1e-7).
  Dense path kept for averaging buffers, custom reductions, and sparse
  weights. Skip the decay multiply when decay is the no-op 1.0. Give
  MSTDPET the same (dt, device)-keyed decay/learning-rate cache as MSTDP
  (was 3 exp() + 2 host-to-device copies per step).
- New tests: rank-1 MSTDP == dense eligibility (batch 1/4, tensor
  rewards), fold-cache storage + invalidation, deferred time-step norm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Hananel-Hazan

Copy link
Copy Markdown
Collaborator

Thank you @C-Earl for the performance gain! much appreciated

@Hananel-Hazan
Hananel-Hazan merged commit bbafb9a into master Aug 4, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants