Repository navigation
Reorder spike vectors with a flexible-dtype numba kernel - #4784
grahamfindlay wants to merge 3 commits into
Conversation
|
The 2 failing tests don't appear to have anything to do with this PR. They're tolerance tests failing on |
|
EDIT: No longer applicable, see below. |
62a512f to
22cd36f
Compare
|
Removed the fix for the unrelated failing tests since another one was already just merged in #4792 |
22cd36f to
89ee24f
Compare
|
@samuelgarcia the failing Python 3.14 tests are unrelated and should be fixed in #4845 so this is still ready for your review anytime |
89ee24f to
98f3b8d
Compare
The counting-sort kernel from SpikeInterface#4695 moved rows through a flat (N, num_fields) int64 view and addressed unit_index and segment_index as columns 1 and 2, essentially assuming minimum_spike_dtype. Samuel suggest that we might want to change the spike vector dtype in the future, so this is a more flexible variant that can take narrow index dtypes (e.g. int8). Also updated the numpy fallback to work for narrow index fields. Measured on ~400M spikes, 342 units: - 11.6 -> 12.0 s (3x int64 fields) - 13.1 -> 13.0 s (4x int64 fields) - 9.1 s (1 each of int64/int32/int8 fields)
for more information, see https://pre-commit.ci
98f3b8d to
99ab7c5
Compare
|
@samuelgarcia Actually, nothing about this had to change, so it is all ready for your review. |
JoeZiminski
left a comment
There was a problem hiding this comment.
cool @grahamfindlay this is very nice! Took me a while to look back and understand the provenance of these changes, in general a cool program of work. I expected this change to require much more than it did, very elegant.
No real comments feel free to ignore renaming suggestions, the only one to really consider is excluding some other dtype than numba can't handle e.g. float16
| any width. Any other fields (e.g. the "channel_index" that | ||
| `to_spike_vector(main_channel_indices=...)` adds) are carried through to the output. | ||
| Anything array-like is accepted and materialised with `np.asarray`, so lazily-backed spike | ||
| vectors work, at the cost of being read into memory. |
There was a problem hiding this comment.
worth mentioning here that if any other fields are objects it forces the slow path?
| in_flat = np.ascontiguousarray(spike_vector).view(np.int64).reshape(num_spikes, num_fields) | ||
| out_flat = np.empty((num_spikes, num_fields), dtype=np.int64) | ||
| spike_vector = np.ascontiguousarray(spike_vector) | ||
| ordered_spikes = np.empty(num_spikes, dtype=spike_vector.dtype) |
There was a problem hiding this comment.
could this be called ordered_spike_vector?
| @jit(nopython=True, nogil=True, cache=False) | ||
| def reorder_spike_vector_numba(in_flat, unit_stride, segment_stride, num_buckets, out_flat, order, counts): | ||
| def reorder_spike_vector_numba( | ||
| spikes, unit_index, segment_index, unit_stride, segment_stride, num_buckets, out, order, counts |
There was a problem hiding this comment.
could spikes be spike_vector and out be output_spike_vector? (not strong on this, but renaming out would be nice)
| Stable counting-sort of a structured spike vector by (unit, segment). | ||
|
|
||
| `unit_index` and `segment_index` are the field views (i.e., zero-copy, strided) of `spikes`. | ||
| They are widened to int64 so narrow fields can't overflow the bucket arithmetic. |
There was a problem hiding this comment.
Could mention here 'They can be of arbitrary integer dtype, and so are widened…"
|
|
||
| `unit_index` and `segment_index` are the field views (i.e., zero-copy, strided) of `spikes`. | ||
| They are widened to int64 so narrow fields can't overflow the bucket arithmetic. | ||
| Rows move as whole records (`out[pos] = spikes[i]`), so any extra fields travel with their spike. |
There was a problem hiding this comment.
I am curious why this doesn't work if the extra fields are objects now
|
|
||
|
|
||
| NARROW_INDEX_DTYPES = { | ||
| "int64/int32/int8": np.dtype([("sample_index", "int64"), ("unit_index", "int32"), ("segment_index", "int8")]), |
There was a problem hiding this comment.
maybe this is done elsewhere, is it also worth testing the all int64 array too?
| """ | ||
| names = dtype.names | ||
| if names is None or names[:3] != tuple(name for name, _ in minimum_spike_dtype): | ||
| if names is None or dtype.hasobject: |
There was a problem hiding this comment.
we could also check:
- dtype is not float16 which numba cannot support, as well as some extended precision float and complex dtype
- dtype.isnative ensures endianness is same as machine in use
Follows @samuelgarcia 's comment on #4695 and #4781.
The numba kernel from #4695 moved rows through a flat
(N, num_fields)int64view and addressedunit_indexandsegment_indexas columns 1 and 2, assumingminimum_spike_dtype.Samuel suggest that we might want to change the spike vector dtype in the future, so this is a more flexible variant that can take narrow index dtypes (e.g. int8).
Also updated the numpy fallback to work for narrow index fields.
Measured on ~400M spikes, 342 units: