Skip to content

perf(core): drop a redundant deepcopy of command kwargs - #7607

Open
dexhunter wants to merge 1 commit into
OpenBB-finance:developfrom
dexhunter:perf/drop-redundant-kwargs-deepcopy
Open

perf(core): drop a redundant deepcopy of command kwargs#7607
dexhunter wants to merge 1 commit into
OpenBB-finance:developfrom
dexhunter:perf/drop-redundant-kwargs-deepcopy

Conversation

@dexhunter

Copy link
Copy Markdown

StaticCommandRunner._execute_func deep-copies kwargs, pops "chart" from the original, then deep-copies again:

kwargs_copy = deepcopy(kwargs)
chart = kwargs.pop("chart", False)
kwargs_copy = deepcopy(kwargs)   # overwrites the line above

The first copy is never read — it is overwritten two lines later — so every command dispatch pays two full deep copies of the caller's payload where one is needed. This removes the dead store.

What it costs today

The copy is of kwargs, so the cost tracks the payload. For obb.equity.price.historical(...)-style commands the kwargs are a handful of scalars and the redundant copy is 2.7 µs — genuinely irrelevant next to the provider HTTP call, and I would not have opened a PR for that alone.

It matters for the commands that take a data payload — technical, econometrics and quantitative all accept list[Data] — because there the payload is the dataset and the computation is local, so there is no network call to hide behind. Measured on the region itself, median of 15 samples per round, 3 interleaved rounds:

payload today (2 copies) this PR (1 copy) saved per call
250 rows 1.45 ms 0.72 ms 0.73 ms
1,000 rows 5.83 ms 2.88 ms 2.95 ms
5,000 rows 30.5 ms 14.6 ms 15.9 ms

Frequency is one per command dispatch, unconditionally — it is not behind the chart branch or any provider check. It also runs synchronously inside async _execute_func, so under the API server it occupies the event loop for that whole duration.

A control confirms where the cost is: keeping both copies but moving the pop first measures 5.87 ms at 1,000 rows, i.e. unchanged. The pop is not the cost; the second copy is.

Behaviour

Unchanged. The removed line's value was overwritten before any read, so the surviving copy — taken after "chart" is popped — is exactly what the rest of the function already used.

openbb_platform/core/tests/app/test_command_runner.py: 25 passed, 1 failed. That one failure, test_static_command_runner_chart, reproduces identically on unmodified develop at 3e071fcc in the same environment, so it is pre-existing and not from this change. I did not touch any test.

What I deliberately left out

Several alternatives are faster than this patch and I am not proposing them:

  • copying via json (1.78 ms) or pickle (0.59 ms) round-trips — both change which payload types survive;
  • skipping the deep copy for the data key, or a shallow copy, or copying only when charting was requested (0.001–0.002 ms) — all alias the caller's data, so a downstream mutation could leak back;
  • the other deepcopy calls in this file, at merge_args_and_kwargs, are not dead — their values flow into parameter_map — so removing them would be a real semantic change rather than a cleanup, and they are out of scope here.

This PR is only the dead store.

Exploration record

Twelve variants of this region were implemented and measured, including the ones above that do not qualify and a no-copy floor reference. Three structurally different ways of expressing "copy once" — deleting the dead store, popping from the copy, and a single copy with an explicit memo — land at 2.881 / 2.860 / 2.858 ms, within 0.8% of each other, so there is one improving move here rather than a spectrum. Public record of that exploration: https://dashboard.weco.ai/share/DJSi5Z0pwQGnRZN3ZJhlFAdbCs8GHIX5

StaticCommandRunner._execute_func deep-copied kwargs, popped "chart" from the
original, then deep-copied again. The first copy was overwritten before it was
ever read, so every command paid two full deep copies of the caller's payload
where one was needed.

For commands that take a data payload - technical, econometrics and
quantitative all accept list[Data] - that is the whole dataset copied twice per
call. Removing the dead store halves it: with a 1,000-row payload the copy step
goes from 5.83 ms to 2.88 ms, and with 5,000 rows from 30.5 ms to 14.6 ms.

Behaviour is unchanged: the removed assignment was dead.
@dexhunter

Copy link
Copy Markdown
Author

A note on why this PR shows no checks at all, because that is easy to read as "nothing has happened here yet".

No workflow has ever run on it. Every job is held in action_required — GitHub queues workflow runs on fork pull requests until someone with write access approves them — so Unit test Platform, General Linting, PR Description Check, Pull Request Labels and CodeQL Advanced have never started. That is an authorisation state, not a signal about the branch.

Since CI could not speak for it, I ran the equivalent locally against develop at 3e071fcc:

  • Still applies. The branch merges cleanly and is not behind develop; it sits directly on that commit.
  • The dead store is still there verbatim, at command_runner.py:330:
    kwargs_copy = deepcopy(kwargs)
    chart = kwargs.pop("chart", False)
    kwargs_copy = deepcopy(kwargs)
    The first assignment is overwritten two lines later before anything reads it. The diff removes that one line and nothing else.
  • Tests, both ways. tests/app/test_command_runner.py on unmodified develop: 1 failed, 25 passed. With this patch applied: 1 failed, 25 passed. Same test either way — test_static_command_runner_chart, failing with AttributeError: 'OBBject' object has no attribute 'charting', which is the charting extension not being installed in a bare core environment. Pre-existing and unrelated to this change; if it passes in your CI then my environment is the odd one out and I would want to know.
  • Lint. ruff check passes on the changed file. black --diff --check proposes the exact same reformatting with and without the patch, so this introduces no formatting delta either.

Happy to fold this into a larger cleanup of that function if you would rather not spend a review on a one-line change, or to close it if the duplication is deliberate and I have misread it.

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