You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ObjectList.append is O(n²): the duplicate check rebuilds a list of ids on every call #1927
Describe the bug
Each append to an ObjectList (and so to Segment.spiketrains, Segment.analogsignals, etc.) takes time proportional to the length of the list. Building a list of n objects therefore takes O(n²). This also slows down any IO that appends objects such as, NixIO.read_all_blocks().
This inefficiency is related to the _contains function (neo/core/objectlist.py:51), called by ObjectList._handle_append. _contains builds a temporary list of the ids of every item on each call and then searches it linearly:
Another solution that reach O(1) is to keep in ObjectList a set of ids alongside _items. That set would have to be updated in every method that mutates _items.
On a (un)related matter (I can create a separated issue for this), extend and __iadd__ don't check for duplicates within the iterable being added (mylist.extend([a, a]) succeeds). I don't think this is the expected behavior.
2500 appends: 0.21s ( 85.7 us per append)
5000 appends: 0.86s ( 172.6 us per append)
10000 appends: 1.75s ( 175.1 us per append)
20000 appends: 7.49s ( 374.7 us per append)
Expected behaviour _contains should be at least O(n)
Environment:
OS: Linux mint 22.3 (Ubuntu 24.04)
Python version: 3.12
Neo version: 0.14.5
NumPy version: 2.5.3
Additional context
This problem was uncovered during the refactor of simulation results structure for the BSB dbbs-lab/bsb#299
I confirmed the "don't check for duplicates" bug, and found several more. I've opened #1935, I plan to fix those first before fixing the performance problem.
@apdavison, now that #1936 is merged, are you already working on the remaining append slowdown?
I have an AI-assisted local patch on top of d50b9b9. It keeps an identity index with the shared list storage, preserving the new duplicate and validation rules. In the issue's append loop, with SpikeTrain objects constructed outside the timer, 20,000 appends take 3.99 s on master vs 14.6 ms with the patch locally (Python 3.12, NumPy 2.5.3; median of three runs).
The tradeoff is O(n) memory: about 2.6 MiB extra for 20,000 objects. Legacy plain-list pickle states keep the linear fallback. The offline suites pass with NumPy 1 and 2, including shared-copy, pickle and reentrant-mutation tests.
Would a focused PR be useful, or would that duplicate work you've already started? Nothing has been pushed yet.
Activity
zm711 commentedon Oct 24, 2026
@apdavison what do you think here?
apdavison commentedon Oct 27, 2026
I confirmed the "don't check for duplicates" bug, and found several more. I've opened #1935, I plan to fix those first before fixing the performance problem.
Fix multiple bugs in ObjectList (#1936)
viranovskaya commentedon Nov 4, 2026
@apdavison, now that #1936 is merged, are you already working on the remaining append slowdown?
I have an AI-assisted local patch on top of d50b9b9. It keeps an identity index with the shared list storage, preserving the new duplicate and validation rules. In the issue's append loop, with SpikeTrain objects constructed outside the timer, 20,000 appends take 3.99 s on master vs 14.6 ms with the patch locally (Python 3.12, NumPy 2.5.3; median of three runs).
The tradeoff is O(n) memory: about 2.6 MiB extra for 20,000 objects. Legacy plain-list pickle states keep the linear fallback. The offline suites pass with NumPy 1 and 2, including shared-copy, pickle and reentrant-mutation tests.
Would a focused PR be useful, or would that duplicate work you've already started? Nothing has been pushed yet.