Skip to content

perf: use lookup maps instead of linear opponent scans in standings widgets#7649

Open
Rathoz wants to merge 1 commit into
standings-tests-aifrom
standings-widget-lookup-maps
Open

perf: use lookup maps instead of linear opponent scans in standings widgets#7649
Rathoz wants to merge 1 commit into
standings-tests-aifrom
standings-widget-lookup-maps

Conversation

@Rathoz

@Rathoz Rathoz commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • The FFA widget previously used Opponent.same inside a per-cell nested loop to find an opponent's entry in each column round, producing O(R²·N²) Opponent.same calls per render. The Swiss widget had the same pattern at O(R·N²). Opponent.same is expensive: it can invoke uncached mw.ext.TeamTemplate.raw PHP callbacks for team opponents, or allocate and sort arrays for party opponents.
  • Both widgets now build a entryByRound table once per standings render: an array (one entry per round) of { [Opponent.toName(entry.opponent)] = entry } maps. Opponent.toName returns a stable identity string (no PHP calls, no allocation). The per-cell lookup becomes a plain O(1) table access, and Opponent.toName(slot.opponent) is hoisted out of each row's per-column closure so it is computed once per row rather than once per column.
  • All rounds of one standings table share the same stored opponent records, so toName keys are consistent across rounds — no fallback needed for entry lookups.
  • The Array.indexOf(match.opponents, …) call inside the Swiss per-cell body (which identifies the opposing player in a 2-opponent match) is deliberately left on Opponent.same: those opponents come from match2 records where renamed-team handling in Opponent.same matters, and it is only ever called on 2 opponents per match, so it is not a performance concern.
  • Rendered output is byte-identical to the original; this is a pure performance change.

How did you test this change?

  • busted --run=ci (full suite): 605 successes / 0 failures / 0 errors — including spec/standings_model_spec.lua, which renders both the FFA and Swiss widgets end-to-end and asserts row ordering and cell content.
  • luacheck on both modified files: 0 warnings / 0 errors.
  • This branch is based on standings-tests-ai (PR test: add integration tests for standings #7647), which adds the widget render tests that lock the behavior being preserved here.

🤖 Generated with Claude Code

@Rathoz Rathoz requested review from a team as code owners June 12, 2026 14:08
…idgets

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Rathoz Rathoz force-pushed the standings-widget-lookup-maps branch from 90dff66 to 40a92a9 Compare June 12, 2026 16:09

@hjpalpha hjpalpha left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm on phone

fwiw it will make salts goal to allow A/B teams stuff harder

@mbergen

mbergen commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

fwiw it will make salts goal to allow A/B teams stuff harder

I'm currently convinced we'd need to support them inside Opponent.toName anyways (as other components, i.e. TeamParticipants use that), so the effect of this is probably not that bad.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants