Skip to content

[ESSREDUCE] Build StreamProcessor on sciline stages - #867

Draft
SimonHeybrock wants to merge 4 commits into
mainfrom
stream-processor-sciline-stage
Draft

SimonHeybrock wants to merge 4 commits into
mainfrom
stream-processor-sciline-stage

Conversation

@SimonHeybrock

@SimonHeybrock SimonHeybrock commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Part of #746. Fixes #868. Supersedes #793, which fixed the same on the old design; its test is included here. Rewrites StreamProcessor on sciline.Stage as planned in sciline ADR 0003 (design doc section 6.6). Draft: needs a sciline release with Stage and scipp/sciline#256, so CI against released sciline will fail until then.

What changed

StreamProcessor splits its workflow into stages built in one prepare_stages call, so everything that depends on neither context nor chunks is still computed once at construction:

  • context stages, computing from the context keys what the other stages read that depends on the context, and context-only targets. There is one per group of values depending on the same context keys, ordered so that a stage runs after those whose results it reads. set_context runs only the stages that read an updated key or a value recomputed before them;
  • one chunk stage per group of accumulators that read the same dynamic keys;
  • a finalize stage, computing the targets from the accumulated values.

Which context-derived values the context stage computes is found from the cached keys of the chunk and finalize stages built on their own (ADR section "Nested loops"). This replaces _FedWorkflow, _build_streaming_workflow, pruning by assigning None, and the graph walks that found context and finalize inputs. visualize uses sciline.visualize_stages with the same node groups and colors, which removes the custom styling and legend code. The public interface is unchanged.

Behavior changes

  • A target that depends on a dynamic key other than through an accumulator raises at construction unless allow_bypass=True. Previously None was passed to the provider at finalize.
  • An error about missing context now names the context keys that were never set.

The visualize tests that called removed private helpers now check the styles of the rendered nodes.

Performance of context updates

set_context timed on the esslivedata LOKI rear detector view in wavelength mode, with live transforms and ROIs:

Update Before Single context stage This PR
detector carriage 86 ms 86 ms 86 ms
sample motor 11–13 ms ~80 ms 7–8 ms
ROI edit 77 ms ~80 ms 1.6 ms
wavelength lookup table 11 ms ~80 ms 1.7 ms

A single context stage, as the sciline design document proposed, rebuilt the detector projection on every sample motor message, although the projection does not depend on the sample. Grouping context-derived values by the context keys they depend on avoids that.

ROI edits got faster than before: the ROI values read the projection, and the old set_context computed them on a workflow that was fed only the context keys, so it rebuilt the projection as an intermediate on every edit and discarded it. Now the ROI stage takes the projection held from the last position update as input. The sample motor and lookup table improvements likely have the same cause; not traced.

Data updates on the same detector view: accumulate is unchanged (about 95 ms per chunk of 100 events, 175 ms for 1M events), while finalize went from 83 ms to 5 ms. The old finalize workflow was fed the context keys and only those context-derived values that are targets, so each finalize recomputed the ROI masks, ROI pixels, and pixel weights, including the projection they read. The finalize stage now takes them as input. Per-call graph building, which Stage.run avoids, cost only about 1 ms per call before, since inputs were already fed without rebuilding the pipeline.

Sciline findings

Test plan

  • essreduce test suite passes with sciline main plus Drop stage inputs the outputs do not need sciline#256.
  • esslivedata test suite against this branch: all pass except one test on an unmerged esslivedata branch that relies on the old None bypass and needs allow_bypass=True.
  • Construction and processing time of the esslivedata workflow tests unchanged.

🤖 Generated with Claude Code

StreamProcessor splits its workflow into sciline.Stage objects built in
one prepare_stages call: a context stage computing what the other stages
read that depends on the context, one chunk stage per group of
accumulators reading the same dynamic keys, and a finalize stage. The
forwarded keys are derived from the cached keys of the chunk and
finalize stages built on their own, as in sciline ADR 0003. This
replaces _FedWorkflow, _build_streaming_workflow, the pruning by
assigning None, and the graph walks for context and finalize inputs.

visualize uses sciline.visualize_stages with the same node groups and
colors, which removes the custom styling and legend code.

Behavior changes:
- A target depending on a dynamic key other than through an
  accumulator raises at construction unless allow_bypass=True, instead
  of passing None to the provider at finalize.
- Updating any context key recomputes everything derived from the
  context, not only what depends on the updated key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@SimonHeybrock SimonHeybrock self-assigned this Nov 12, 2026
@github-actions github-actions Bot added the essreduce Issues for essreduce. label Nov 12, 2026
@github-actions github-actions Bot changed the title Build StreamProcessor on sciline stages [ESSREDUCE] Build StreamProcessor on sciline stages Nov 12, 2026
With sciline stages dropping unneeded inputs, StreamProcessor no longer
builds stages only to find exact inputs: the finalize stage is built
once with all accumulation and dynamic keys as candidates, and its
inputs give the accumulators it reads and the bypassed dynamic keys.

An accumulator that only feeds another accumulator no longer fails at
construction; it is computed per chunk as before and its accumulator is
dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One context stage recomputed every context-derived value on any context
update. On the LOKI rear detector in esslivedata, with live sample
motor transforms, each sample motor message then rebuilt the detector
projection, which does not depend on the sample: about 80 ms per batch
instead of 12 ms.

Context-derived values are now grouped by the context keys they depend
on, one stage per group. A group reading the result of another depends
on a strict superset of its keys, so ordering by the number of keys
runs each stage after those it reads. set_context runs the stages that
read an updated key or a value recomputed before them. A group that
reads a value derived in another group takes it as input instead of
recomputing it, so an ROI edit no longer rebuilds the projection either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Taken from #793. It checks, by counting provider calls, that neither
accumulate nor finalize recomputes a value derived from the context, for
every combination of who reads it: an accumulator, a target computed from
the accumulators, or the value being a target itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

essreduce Issues for essreduce.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StreamProcessor.finalize recomputes context-derived values on every call

1 participant