Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙️ Pengaturan Proxy Target

Skip to content

[dbsp] Skip a recursive sub-circuit at a step where its inputs did not change #7744

Open

Description

@anandbraman

Summary

A recursive sub-circuit runs a full fixpoint at every step of its parent circuit, also when none of its imported inputs changed. In transaction mode one commit takes many root steps, so most of these runs do no useful work. In one customer pipeline, these idle runs are about 83% of the commit time (15 s of 18 s for a 4 to 15 row transaction).

Proposal: when no worker has a change on any import of the sub-circuit, and the sub-circuit has no pending work, do not run the nested transaction. Write the zero result to the export streams, and report the flush as complete.

Current behavior

Step What happens Code
1 ChildNode::eval runs the import nodes, then calls executor.transaction(). It does not look at the inputs. circuit_builder.rs#L7343-L7355
2 IterativeExecutor::transaction runs nested transactions until termination_check is true, so at least one. schedule.rs#L402-L424
3 Each nested transaction steps until the inner commit is complete. schedule.rs#L281-L291
4 A ShardedAccumulatorReceiver completes its flush only when the flushes of all peers are in. They arrive after the end-of-step consensus, so each receiver on the longest inner path adds one inner step. sharded_accumulator.rs#L1293-L1321, dynamic_scheduler.rs#L556-L575

Result: an idle run costs (inner receiver chain + 1) inner steps. Each inner step evaluates all operators of the sub-circuit and ends with a consensus across all workers.

Evidence

Source: a customer support bundle, v0.358.0, 8 workers, 1 host. The program has 113 views and 6 recursive groups. Input comes through HTTP ingress transactions of 4 to 15 rows. The window holds 8 commits and 761 root steps (95 per commit). Times are from the slowest worker.

Recursive group DBSP operators Inner receiver chain Inner steps per run, predicted / measured Runs Input rows in window Wall clock per commit Share of commit
A 478 7 8 / 8.5 761 30,300 4.7 s 26%
B 129 6 7 / 9.1 761 45,000 3.0 s 16%
C 129 6 7 / 7.2 761 612 2.4 s 13%
D 192 5 6 / 6.0 761 0 2.2 s 12%
E 127 7 8 / 8.3 761 27,900 1.8 s 10%
F 89 4 5 / 5.1 761 3,264 1.1 s 6%
Root circuit, other ~11k operators n/a 94 (root) n/a n/a n/a 3.1 s 17%
Total 18.1 s (measured 18.0 s) 100%
  • Each group runs at every root step: runs = 761 = root steps.
  • Group D got 0 input rows in the window. It still cost 2.2 s per commit.
  • The measured iterations per run match (inner chain + 1) for all 6 groups. A and B are higher because some runs had data and needed more than one inner transaction.
  • Model: commit = (root chain + 1) x (root step cost + sum over groups of (inner chain + 1) x inner step cost) = 95 x (0.033 s + 0.158 s) = 18.1 s.

Expected effect (estimate, not measured)

We expect each import to carry data at a small number of root steps per commit (k), because the accumulator before it emits once per commit. If each group runs only at those k steps:

Now After (k = 1 to 3)
Recursion cost per commit 95 x 0.158 s = 15 s 0.2 to 0.5 s, plus the real fixpoint work
Commit time 18 s about 3.5 to 4 s

The bundle cannot show k directly. #7743 adds profile counters for it (subcircuit_runs_count, subcircuit_runs_with_input_count) and a DBSP test, run_stats::recursion_runs_at_every_root_step. In that test, with 4 workers and a chain of 8 joins, a commit that does not touch the recursion's input runs the recursion at all of its 7 to 9 root steps, with no input on any worker. A commit that does touch it gives the recursion input in 1 of 9 runs. So k = 1 in the test.

Proposal

In ChildNode::eval (or in IterativeExecutor):

  1. After the import nodes run, each worker checks if all of its imports are empty in this step. For Delta0 this means an empty batch or no value.
  2. All workers agree on one boolean: if any worker has a change, all workers run. The decision must be the same on all workers, because the inner exchanges and the inner commit consensus need every worker. One consensus round replaces (inner chain + 1) inner steps.
  3. If no worker has a change, the sub-circuit is at its fixpoint from the last run, and no inner operator has pending work, then:
    • do not call executor.transaction();
    • write the zero value to each export stream. Today FeedbackOutputNode::clock_end writes it (circuit_builder.rs#L6881-L6887). If nothing writes it, the parent reads a stale value or no value;
    • if the parent flushed the ChildNode, report the flush as complete.

Things to check

  • Clock. clock_end advances the nested clock (circuit_builder.rs#L3863-L3870). Inner traces use it for timestamps, and the sub-circuit checkpoint stores it (circuit_builder.rs#L7402). A skipped run does not advance it. Confirm that inner traces and checkpoint restore accept this.
  • Imports other than Delta0, for example traces of the parent, need their own test for "changed".
  • Bootstrap and replay of recursive views must not skip. This includes concurrent bootstrap and swap_state, where the nested clock is realigned.
  • Inner operators that report fixedpoint() == false at the end of a run, or that have pending output or are async and not ready, must force a run.
  • Multihost: the agreement in step 2 must include all hosts.

Acceptance criteria

  • A transaction that does not change the inputs of a recursive view does not start a nested transaction for it. The steps_count of that scope in the circuit profile does not grow, except at the steps where its imports have data.
  • The outputs do not change. The existing recursive tests pass. Add a test that commits transactions which do not touch the recursion's inputs, and check the scope's step count and the outputs.
  • Works with more than one worker and with more than one host.

Related

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

Metadata

Metadata

Labels

DBSP coreRelated to the core DBSP library

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions