Skip to content

Power Report Performance - Skip nodes with 0 value. - #10383

Open
AdamZ-8113 wants to merge 5 commits into
PathOfBuildingCommunity:devfrom
AdamZ-8113:perf/power-report-relevance-pruning
Open

AdamZ-8113 wants to merge 5 commits into
PathOfBuildingCommunity:devfrom
AdamZ-8113:perf/power-report-relevance-pruning

Conversation

@AdamZ-8113

Copy link
Copy Markdown
Contributor

Description of the problem being solved:

This PR makes it so that power reports skip nodes that cannot affect the calculation. It is built on top of several other PRs I've submitted. #10313 and #10373 which I hope will be accepted/merged, and more important #10381 because without it, the results of power reports are wrong. I discovered the issue in 10381 while working on this PR, because I was getting correctness errors.

Transparently I understand a lot less of the code here than I do on the other PRs, this is exclusively Codex driven. The total lines of code changed for just the work to skip nodes that can't affect the calculation are 623 additions, 216 deletion. The other edits are from the other PRs. If there's a more preferable way to submit this, let me know.

The check to skip nodes records which modifier lookups run during a calculation, then checks whether a candidate node could supply a matching modifier. When the checks establish that adding the node cannot change the output, Power Report reuses the unchanged result.

How the new check works

  1. Run one calculation without adding or removing nodes. During this pass, the PR enables recording inside the existing modifier lookup functions in ModStore.lua, including Sum, More, Flag, Override, List, Tabulate, and HasMod.

  2. Record the arguments passed to those functions. For example, CalcOffence.lua calls skillModList:Sum("INC", cfg, "Speed") when calculating increased/reduced speed. The recording captures Speed, the INC modifier type, and the flags used to filter applicable modifiers. These are recorded before the lookup runs, so a lookup returning zero is still included: a newly added node could provide a modifier that the build currently lacks. Recording also covers checks for passive IDs that are currently unallocated.

  3. Construct each candidate node's contribution using existing PoB code. The new probeNodeContribution function calls buildModListForNode on a temporary copy of the node. This produces its actual calculation modifiers rather than relying on its displayed text. The probe also includes allocation-count effects, such as increasing the number of allocated notables or masteries. It doesn't change the live passive tree.

  4. Compare the candidate's modifiers against the recorded lookups. The new CalcRelevance.lua module checks modifier names, types, and flags. Any possible match keeps the normal calculation, even if that calculation might ultimately return zero. A recorded allocation check for that node also prevents skipping it. Only an eligible candidate with no possible match can reuse the unchanged output.

This does not introduce a predefined list of modifiers for Hit DPS, Life, or another report metric. The record comes from the lookup functions executed for the current build. It includes lookups performed by nested minion and Full DPS calculations.

Where it keeps the normal calculation

Only pure node additions can be skipped. Removing nodes or combining additions with other changes still uses the calculator. For a path containing several nodes, every node must pass the relevance checks.

The check also falls back to normal calculation for radius-jewel interactions, nodes that grant skills or produce explosion effects, and candidates it cannot safely inspect. If the recording pass encounters a reused skill calculation whose lookups weren't recorded, pruning is disabled for that report; the Manaforged and Reflection cache paths have explicit guards for this.

The recording and node classifications are rebuilt for each report run. A node is never skipped simply because it produced a zero result previously.

Steps taken to verify a working solution:

  • Compared the existing 3 PR stack (baseline) against the same stack plus this PR added. This isolates the improvement from node pruning.
  • Tested 50 build entries representing 48 distinct builds, with two randomly selected metric/depth combinations per entry. Each configuration ran once cold and twice warm on both versions: 600 executions and 300 paired comparisons.
  • All 300 comparisons matched exactly for node and path powers, mastery alternatives, cluster powers, report row identities and scores, and report maxima. Ordering among tied rows was not compared.
  • Measured a 15.7% reduction in warm report time, using the geometric mean across the sampled comparisons. This is the incremental improvement from this PR, not the improvement from the complete optimization bundle.
  • Ran the full LuaJIT regression suite: 620 tests passed, including coverage for recorded lookups, allocation dependencies, nested calculations, cached-calculation fallbacks, and pruned-versus-unpruned reports.
  • Preserved the build inputs, source snapshots, test selections, timings, outputs, and Python script for reproduction -- happy to share as needed

Once the code from this improvement was tested in isolation, I ran a comparison of all performance PRs together against a clean upstream/dev (true baseline). Across 50 build entries and 100 report configurations, the complete bundle reduced warm Power Report time by 49.5% (1.98x speedup) and cold report time by 48.3%. It was faster in 295/300 paired measurements.

This was all synthetic testing, so I took a side by side video to demonstrate the total speedup https://www.youtube.com/watch?v=XV2QHYDgrFY . I'm still clicking around in the video so timing isn't perfectly synced, but the right side shows the dev branch which is visibly much faster across several builds and tests.

Link to a build that showcases this PR:

  • Can share the python test script/builds if needed. Otherwise test on (m)any builds.

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.

1 participant