Skip to content
Skills Library
Programming · Review & audit

cpp-performance

/cpp-performance

Detect and fix performance smells in C++ code in this repository: O(n^2) loops, unnecessary copies and allocations (outside hot paths, redundant copies of small trivial types are permitted for clarity), std::map vs std::unordered_map, virtual calls and std::function in hot paths, indicator/data layer recomputation, expensive synchronization, and string concatenation inside loops. Suggest the right algorithm, container, view, or cache. Use when reviewing or writing code in tight loops (engine bar-loop, indicators, replay, data prefetch), when benchmarks regress, or when the user mentions slow,

Licence
Apache-2.0
Last repository change
24.10.2026

Assessment and security

100/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
1
Scripts
0
HTML comments
0
URLs in SKILL.md
0
Deprecation

No signals detected. Repository changed on 24.10.2026, without references to retired models or APIs.

Licence

Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.

What it does and when to use it

Detect and fix performance smells in C++ code in this repository: O(n^2) loops, unnecessary copies and allocations (outside hot paths, redundant copies of small trivial types are permitted for clarity), std::map vs std::unordered_map, virtual calls and std::function in hot paths, indicator/data layer recomputation, expensive synchronization, and string concatenation inside loops. Suggest the right algorithm, container, view, or cache. Use when reviewing or writing code in tight loops (engine bar-loop, indicators, replay, data prefetch), when benchmarks regress, or when the user mentions slow,

Original author description

Detect and fix performance smells in C++ code in this repository: O(n^2) loops, unnecessary copies and allocations (outside hot paths, redundant copies of small trivial types are permitted for clarity), std::map vs std::unordered_map, virtual calls and std::function in hot paths, indicator/data layer recomputation, expensive synchronization, and string concatenation inside loops. Suggest the right algorithm, container, view, or cache. Use when reviewing or writing code in tight loops (engine bar-loop, indicators, replay, data prefetch), when benchmarks regress, or when the user mentions slow,

How it works

  1. Detection checklist (smells to flag in review)
  2. Caching, when it earns its keep
  3. Compile-time levers
  4. Measurement
  5. Profile, don't guess
  6. Anti-patterns: don't do these in this repo
  7. Verification before committing
Original SKILL.md headings · 207 lines, 1,516 words
SKILL.md preview
---
name: cpp-performance
description: >-
  Detect and fix performance smells in C++ code in this repository: O(n^2) loops,
  unnecessary copies and allocations (outside hot paths, redundant copies of small
  trivial types are permitted for clarity), std::map vs std::unordered_map, virtual
  calls and std::function in hot paths, indicator/data layer recomputation,
  expensive synchronization, and string concatenation inside loops. Suggest the
  right algorithm, container, view, or cache. Use when reviewing or writing code
  in tight loops (engine bar-loop, indicators, replay, data prefetch), when
  benchmarks regress, or when the user mentions slow, hot path, optimize,
  benchmark, profile, allocation, or cache.
---

# C++ Performance

Performance work is **measure → fix → re-measure**, not "I think this is faster". Every claim has a number behind it.

The accepted measurable performance budget is in `Docs/Specs/DataLayer.md`.
The engine specification identifies behavior whose implementation may become a
hot path but does not itself define a numeric budget. Relevant paths are:

- **Engine bar loop** (`Docs/Specs/EngineReplayPnL.md` §2): one canonical event sequence per slice.
- **Indicators `update()`** (`Docs/Specs/Indicators.md`): O(1) amortized; no allocation after warm-up.
- **Replay presentation** and **Data prefetch** become hot paths only when a
  checked-in benchmark or profile identifies them as such.

Code that runs once at startup or rarely is **not** a hot path. Don't micro-optimize it.

## Detection checklist (smells to flag in review)

When reading or writing C++, scan for these. Each one needs justification or a fix.

### Containers and lookups

| Smell | Why it's slow | Fix |
|---|---|---|
| `std::map` for measured hot lookups that do not need ordering | tree traversal and poor locality | consider `std::unordered_map` after measuring and reviewing determinism |
| `std::list` / `std::forward_list` | terrible cache locality | `std::vector`, `std::deque` |
| `std::unordered_map<std::string, V>` with frequent lookup by `string_view` | allocates a temporary `std::string` per lookup unless using transparent hashers | use heterogeneous lookup with `transparent` hasher/equal |
| Linear `std::find` on a sorted container | O(n) when O(log n) is free | `std::ranges::lower_bound` / `binary_search` |
| `vector::push_back` in a tight loop without `reserve()` | repeated reallocation | `reserve(n)` before the loop |
| `vector` of polymorphic objects | cache-unfriendly indirection | `std::variant` if the alternatives are known; otherwise vector of pointers grouped by type |

### Copies and allocations

**Outside hot paths**, redundant or otherwise “unnecessary” copies are
**permitted** when they simplify an API or control flow: pass-by-value
parameters for small trivial values, named temporaries, and returned structs.
Reserve strict copy avoidance for measured hot paths identified under
[`Docs/DefinitionOfDone.md`](../../../Docs/DefinitionOfDone.md), or when the
payload is large.

| Smell | Fix |
|---|---|
| `void f(std::string s)` taking by value when caller has a long string | `std::string_view` if read-only, `const std::string&` if must own; or sink-copy by value when you'll move it into a member |
| `void f(std::vector<T> v)` for read-only | `std::span<const T>` |
| `auto v = computeVector();` then iterating once | range / view chain; avoid materializing |
| `std::string` concat in a loop with `+` | `std::string out; out.reserve(n); for (...) out.append(...);` or `std::format_to(std::back_inserter(out), ...)` |
| `return std::move(x)` of a local | RVO already happens; `std::move` here disables RVO |
| `std::shared_ptr` where `std::unique_ptr` works | avoid the atomic refcount |
| Creating `std::function<...>` in a hot loop | use a templated callable / concrete type / `function_ref` (cheap view) |
| Capturing by value in a lambda when reference is fine and lifetime is OK | `[&]` for local-scope lambdas |
| Defining lambdas inside a hot loop | lift them outside the loop |

### Virtual / indirect calls

`virtual` per bar is fine; per tick of an inner loop is not.

| Smell | Fix |
|---|---|
| Virtual call inside indicator `update()` ring-buffer push | inline the buffer logic; use CRTP if the type is known at compile time |
| `std::function` in the engine's per-bar callback | switch to a concrete callable or compile-time policy |
| `dynamic_cast` in hot path | redesign with `std::variant` + `std::visit` |

### Algorithms

| Smell | Fix |
|---|---|
| Two nested loops where one is hash-able | hashmap → O(n) |
| Re-sort the same data each bar | sort once, then update incrementally |
| Recompute an SMA/EMA from scratch every bar | use the rolling indicator API (`Docs/Specs/Indicators.md`) — never recompute |
| `std::sort` followed by `std::unique` | `std::set` if order doesn't matter; or sort+unique only if you need it once |
| `std::accumulate` in a hot loop with default `+` on `double` over a long sequence | use Kahan summation or pairwise summation if precision matters |

### I/O and DB

| Smell | Fix |
|---|---|
| Per-record console or file output in a hot loop | batch structured records and flush outside the loop using an implemented logging seam |
| DuckDB query per bar | release execution streams immutable segments; DuckDB is developer-only (`Docs/Specs/DataLayer.md`) |
| File open/close per bar | hold the handle for the lifetime of the run |
| `std::ifstream` line-by-line with operator>> | `std::getline` + parse, or memory-map for huge files |
| JSON parsed once per bar | parse once at strategy init |

### Strings

| Smell | Fix |
|---|---|
| `std::string` member where `std::string_view` view suffices | view (only if lifetime is clear) |
| Implicit `std::string` from `const char*` in lookups | `std::string_view` parameters + transparent hasher |
| `to_string` of an int in a hot loop | `std::format_to(buf, "{}", n)` into pre-allocated buffer |

### Synchronization

| Smell | Fix |
|---|---|
| `std::mutex` where reads dominate | `std::shared_mutex` |
| Lock around computation that doesn't touch shared state | snapshot under lock; release; compute outside |
| Atomic counter incremented in a tight loop by every thread | per-thread counter + final reduce |
| `condition_variable.wait` without a predicate | always pass a predicate to handle spurious wakeups |

### Memory layout

| Smell | Fix |
|---|---|
| Struct with members in random order, `sizeof` larger than needed | reorder for alignment (largest first) |
| Cold and hot fields in same struct | split — keep hot fields cache-resident |
| Polymorphic AoS for hot loop | SoA (struct-of-arrays) |
| Bool arrays as `std::vector<bool>` proxies | `std::vector<char>` or `std::array<bool, N>` |

## Caching, when it earns its keep

Add a cache only when:

1. The computation is measured to dominate runtime.
2. The cache hit rate is provably > 50% on real workloads.
3. Cache invalidation has a defined trigger (data version bump, time bound, LRU).

Accepted cache constraints:

- Cache only when the owning spec and a measured caller define an invalidation and memory policy.
- Planned indicators use bounded display history (`Docs/Specs/Indicators.md` §4).
- Replay checkpoints are persisted engine records, not a license to invent an in-memory cache contract.

If you're proposing a new cache, write down: keys, eviction policy, max size, invalidation trigger, expected hit rate. No "future work" caches.

## Compile-time levers

Use a checked-in optimized preset for measurements. Do not invent compiler
flags, enable LTO, force inlining, or add branch hints without before/after
evidence and verification on the supported toolchain.

## Measurement

No checked-in benchmark framework currently exists. For a performance claim,
use a repository benchmark when one is added or document a reproducible command,
fixture identity, machine/runner class, build preset, repetitions, and raw
before/after samples in the PR. Do not invent a benchmark target or dependency.

This framework-free timing skeleton is illustrative C++20, not a repository
benchmark API. It shows repeated raw-sample capture; a real benchmark must also
use a deterministic fixture, consume observable output, warm up when justified,
and report the environment described above.

```cpp
template <typename Value>
struct TimingSamples final {
    std::vector<std::chrono::nanoseconds> durations;
    std::vector<Value> outputs;
};

template <typename Operation>
    requires(!std::same_as<std::remove_cvref_t<
                               std::invoke_result_t<Operation&>>,
                           void> &&
             std::movable<std::remove_cvref_t<
                 std::invoke_result_t<Operation&>>> &&
             std::constructible_from<
                 std::remove_cvref_t<std::invoke_result_t<Operation&>>,
                 std::invoke_result_t<Operation&>>)
[[nodiscard]] auto measureSamples(Operation operation, std::size_t repetitions) {
    using Value = std::remove_cvref_t<std::invoke_result_t<Operation&>>;
    TimingSamples<Value> samples;
    samples.durations.reserve(repetitions);
    samples.outputs.reserve(repetitions);

    for (std::size_t index = 0; index < repetitions; ++index) {
        const auto startedAt = std::chrono::steady_clock::now();
        Value output{std::invoke(operation)};
        const auto finishedAt = std::chrono::steady_clock::now();
        samples.durations.push_back(finishedAt - startedAt);
        samples.outputs.push_back(std::move(output));
    }

    return samples;
}
```

Do not call a performance budget merge-blocking until `Docs/Specs/CiDevFlow.md`
and the checked-in workflow both say it is implemented.

## Profile, don't guess

Before "optimizing", profile:

- Linux: use `perf record -g` with an executable discovered in the checkout,
  then inspect it with `perf report`; do not copy an illustrative target name.
- macOS: Instruments → Time Profiler → attach to process.
- Windows: WPA / VTune.

If the bottleneck isn't where you thought, your optimization plan changes. Don't waste effort on cold code.

## Anti-patterns: don't do these in this repo

- "Optimizing" without measuring. Doesn't go in.
- Hand-rolled SIMD before exhausting `std::ranges` and compiler vectorization. The compiler is good at autovectorizing aligned `std::span<double>` loops if you write the simple form.
- Lock-free data structures without a coverage-quality test suite + TSan + a written invariants doc. The number of times "lock-free" code is correct on the first try is approximately zero.
- Premature templating "in case we need it polymorphic". Add the polymorphism when you have the second use case.
- Caching at every layer "to be safe". Each cache costs invalidation complexity.

## Verification before committing

1. Did you change a documented or measured hot path? → run the verified
   benchmark or provide the reproducible measurement above.
2. Did you add an allocation inside a per-bar or per-slice loop? → justify or remove.
3. Did you add a `std::function` / virtual call inside a tight loop? → replace.
4. Did you introduce a `std::map`? → was `std::unordered_map` considered?
5. Does the canonical determinism test still pass (`Docs/Specs/EngineReplayPnL.md` §9)? Optimizations sometimes change FP order.

Versions

The database has no other skills named cpp-performance.

Database snapshot: 13.11.2026 · package version 8e196dda8278 (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100go-performance
    Use when optimizing slow or performance-critical Go code, allocations, string concatenation in loops, or benchmarks. Also use when writing or reviewing a cache, including implementing one from a stub — its key, its expiry, and how a miss loads. Concurrent code patterns belong to go-concurrency.
  • 100memory-allocation-analyzer
    Find unnecessary heap allocations in C# hot paths — LINQ in loops, boxing, closures, string concatenation, params arrays, async state machines — and rewrite to reduce GC pressure. Use this skill whenever the user wants to reduce allocations, cut GC pressure, find…
  • 100go-performance
    Use when optimizing Go code, investigating slow performance, or writing performance-critical sections. Also use when a user mentions slow Go code, string concatenation in loops, or asks about benchmarking, even if the user doesn't explicitly mention performance patterns. Does…
  • 95cpp
    Modern C++ (C++17/20/23) language and standard library. Covers RAII, move semantics, smart pointers, templates, concepts, ranges, coroutines, modules, std::expected, std::format, std::span, std::string_view. USE WHEN: user mentions "C++", "modern C++", "C++17/20/23", "RAII",…
  • 100modern-cpp
    Guides C++ code toward modern idioms (C++20/23/26). Use when writing new C++ code, modernizing legacy patterns, or working on security-critical C++. Replaces raw pointers with smart pointers, SFINAE with concepts, printf with std::print, error codes with std::expected.
  • 100performance-analyst
    Use when reviewing hot paths, slow code, database queries, N+1 risks, memory usage, loops, I/O, caching strategy, concurrency, latency-sensitive paths, or resource efficiency.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.