Skip to content

Eliminate redundant array copies and vectorise marker scanning in binary event processing - #1

Draft
simonge with Copilot wants to merge 2 commits into
masterfrom
copilot/improve-binary-data-reading
Draft

simonge with Copilot wants to merge 2 commits into
masterfrom
copilot/improve-binary-data-reading

Conversation

Copilot AI commented Sep 3, 2026

Copy link
Copy Markdown

Python processes AcquDAQ events orders of magnitude slower than C++ largely because np.delete(arr, []) was called three times per clean event (allocating three full copies of the event array for nothing), and per-event helper functions used Python for loops with list += range(…) to build index arrays.

Key changes

Acqu.py — skip copies when there is nothing to delete

# Before: always copies, even with an empty index list
eventArray = np.delete(eventArray, scalerIndices)

# After: skip the allocation entirely on the common clean-event path
if len(scalerIndices):
    eventArray = np.delete(eventArray, scalerIndices)

Same guard applied to EPICS and error indices. Also moves the error-path np.delete inside the early-return branch so the copy is only paid when the event is being discarded. ~6× speedup on the hot path (no special markers).

Also replaces np.append([1], …) with np.concatenate(([1], …)) in runFunction to avoid an extra intermediate allocation per buffer.

Mk1Format.py / Mk2Format.py — vectorise CheckErrors and FillScalerArray

Replace for … : list += range(…) with NumPy broadcasting and a fully-vectorised ragged-range built from np.repeat + np.cumsum:

# CheckErrors — before
for errorMark in np.where(dataArray == EReadError)[0]:
    errorIndices += range(errorMark, errorMark + 5)

# CheckErrors — after
errorMarks = np.where(dataArray == EReadError)[0]
if not len(errorMarks):
    return []
return (errorMarks[:, np.newaxis] + np.arange(5, dtype=np.intp)).ravel()
# FillScalerArray — before
for indeces in scalerLocations:
    scalerHeaders += [indeces[0], indeces[0] + 1]
    scalerIndices += range(indeces[0] + 2, indeces[1])

# FillScalerArray — after (ragged range, no Python loop)
counts   = (ends - starts - 2).astype(np.intp)
bases    = np.repeat(starts + 2, counts)
cum      = np.concatenate([[0], np.cumsum(counts[:-1])])
offsets  = np.arange(total, dtype=np.intp) - np.repeat(cum, counts)
scalerIndices = bases + offsets

Mk2Format.pyFillEPICSArray type consistency

Replaces per-buffer epicsIndices += range(…) with append(np.arange(…)) + a single np.concatenate at the end. Empty result now returns np.array([], dtype=np.intp) instead of [] so callers always receive a NumPy array.

Copilot AI and others added 2 commits March 23, 2026 22:10
…se marker scanning

Co-authored-by: simonge <5457667+simonge@users.noreply.github.com>
Agent-Logs-Url: https://github.com/simonge/AcquPy/sessions/8ea991c6-e373-4261-8041-56699093856d
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