Skip to content

feat(memory): apply kernel memory tunables before memory-mode runs - #507

Open
not-matthias wants to merge 2 commits into
mainfrom
cod-3300-kernel-memory-tunables
Open

feat(memory): apply kernel memory tunables before memory-mode runs#507
not-matthias wants to merge 2 commits into
mainfrom
cod-3300-kernel-memory-tunables

Conversation

@not-matthias

@not-matthias not-matthias commented Aug 18, 2026

Copy link
Copy Markdown
Member

Benchmark repos running codspeed --mode memory currently need a hand-written CI step to stabilise kernel memory behaviour before measuring: disable THP, zero vm.compaction_proactiveness / vm.swappiness / kernel.numa_balancing, swapoff -a, and drop the page cache before each suite.

The runner now does this itself at the start of every memory-mode execution.

Behaviour

  • Applied only when a CI run environment is detected, so developer machines are not silently mutated. Skipped entirely when privileges cannot be elevated without a password prompt.
  • Best effort: a knob that cannot be applied is a warning, never fatal.
  • MemoryTunables::apply() returns a guard holding the previous value of every knob it changed, and restores it on drop — including on the error paths, where teardown() never runs. Only knobs that were not already at the target are captured, and THP is restored to the exact mode it held (madvise stays madvise).
  • swapoff -a is guarded: skipped on zram devices (it permanently resets their disksize) and whenever the swapped pages would not fit in MemAvailable. Restoring re-enables the recorded entries one by one, since swapon -a would miss a swap file absent from /etc/fstab.
  • The page cache drop has no previous state to restore; the node is a write-only trigger.
  • Applied in run() rather than setup(), so the page cache is dropped before every suite and --skip-setup does not bypass it. The sysctl/THP writes are no-ops on later suites since ensure_sysctl compares before writing.

Commits

  1. refactor(executor): move linux_sysctl into executor helpers — pure move, the sysctl primitive is no longer walltime-specific.
  2. feat(memory): apply kernel memory tunables before memory-mode runs

Verification

  • cargo clippy --all-targets clean, cargo test --lib (pre-existing failures on this host are the sudo-requiring tests, which write kernel.kptr_restrict and are identical on main).
  • Local non-CI run logs Not running in CI, skipping kernel memory tunables; host THP unchanged.
  • Privileged container: THP enabled/defrag become [never], vm.compaction_proactiveness = 0, vm.swappiness = 0, and the swap guard correctly logged Leaving swap enabled: swapped pages do not fit in available memory.

The sysctl read-then-write-with-sudo primitive is not walltime-specific;
memory mode needs it too.
@codspeed-hq

codspeed-hq Bot commented Aug 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 14 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing cod-3300-kernel-memory-tunables (3568cec) with main (3798897)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@not-matthias
not-matthias force-pushed the cod-3300-kernel-memory-tunables branch from febfce2 to a219b76 Compare August 19, 2026 15:40
@not-matthias
not-matthias marked this pull request as ready for review August 19, 2026 15:45
Disables transparent huge pages, sets vm.compaction_proactiveness,
vm.swappiness and kernel.numa_balancing to 0, disables swap and drops the
page cache, so benchmark repos no longer need a hand-written CI step.

Applied only in CI, best-effort: a knob that cannot be set is a warning.
swapoff is skipped on zram devices and whenever the swapped pages would not
fit in available memory.
@not-matthias
not-matthias force-pushed the cod-3300-kernel-memory-tunables branch from a219b76 to 3568cec Compare August 19, 2026 15:46
@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds best-effort kernel memory tuning around memory-mode benchmark execution and moves shared sysctl support into executor helpers. It also adds CI gating and restoration guards, but CircleCI is omitted and two host-state restoration paths remain unsafe.

  • Applies THP, sysctl, swap, and page-cache tuning before each memory suite
  • Restores captured THP, sysctl, and swap state through a Drop guard
  • Generalizes Linux sysctl helpers for memory and wall-time executors
  • Adds a run-environment predicate controlling whether host tuning is enabled

Confidence Score: 2/5

The PR should not merge until CircleCI receives the intended tuning and host swap/kernel state is reliably recoverable after partial swapoff failure and forced termination.

The new feature silently skips a supported CI provider and can leave persistent runners with altered host-global memory settings through two reachable cleanup gaps.

Files Needing Attention: src/executor/memory/tunables.rs, src/run_environment/mod.rs

Important Files Changed

Filename Overview
src/executor/memory/tunables.rs Adds the complete tuning guard, but aggregate swapoff failure can lose restoration state and process termination bypasses the only cleanup path.
src/run_environment/mod.rs Adds the CI predicate but omits the already-supported CircleCI provider, disabling the feature there.
src/executor/memory/executor.rs Correctly scopes the guard around each memory run, including ordinary error returns, but necessarily depends on the guard's cleanup guarantees.
src/executor/helpers/linux_sysctl.rs Generalizes sysctl mutation to return previous values while preserving existing wall-time setup behavior.
src/executor/wall_time/profiler/perf/mod.rs Updates the profiling sysctl import after moving the helper.
src/executor/wall_time/profiler/samply/mod.rs Updates the profiling sysctl import after moving the helper.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[MemoryExecutor::run] --> B{Supported CI detected?}
  B -- No --> C[Run without tunables]
  B -- Yes --> D{Passwordless elevation?}
  D -- No --> C
  D -- Yes --> E[Capture and apply THP/sysctl/swap state]
  E --> F[Drop page cache]
  F --> G[Run memtrack benchmark]
  G --> H[MemoryTunables::drop]
  H --> I[Restore swap, sysctls, and THP]
  G -. process termination .-> J[Restoration bypassed]
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
### Issue 1
src/run_environment/mod.rs:58-60
**CircleCI bypasses memory tuning**

When a memory-mode run executes on CircleCI, `is_ci_environment()` returns false because it omits the supported CircleCI provider, causing all THP, sysctl, swap, and page-cache stabilization to be skipped.

```suggestion
pub fn is_ci_environment() -> bool {
    BuildkiteProvider::detect()
        || CircleCIProvider::detect()
        || GitHubActionsProvider::detect()
        || GitLabCIProvider::detect()
}
```

### Issue 2
src/executor/memory/tunables.rs:145-148
**Failed swapoff loses restoration state**

If `swapoff -a` disables one swap entry and then fails on another, this branch discards the previously captured entry list, so `Drop` never re-enables the entry already disabled and leaves the host partially swap-disabled.

### Issue 3
src/executor/memory/tunables.rs:48-53
**Forced termination bypasses restoration**

If a memory run is cancelled or killed on a persistent self-hosted CI runner after these host-global settings are applied, process termination bypasses the sole `Drop` restoration path, causing later jobs to inherit disabled swap and altered THP, compaction, swappiness, or NUMA-balancing settings.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: a219b76 | Re-trigger Greptile

Comment on lines +58 to +60
pub fn is_ci_environment() -> bool {
BuildkiteProvider::detect() || GitHubActionsProvider::detect() || GitLabCIProvider::detect()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 CircleCI bypasses memory tuning

When a memory-mode run executes on CircleCI, is_ci_environment() returns false because it omits the supported CircleCI provider, causing all THP, sysctl, swap, and page-cache stabilization to be skipped.

Suggested change
pub fn is_ci_environment() -> bool {
BuildkiteProvider::detect() || GitHubActionsProvider::detect() || GitLabCIProvider::detect()
}
pub fn is_ci_environment() -> bool {
BuildkiteProvider::detect()
|| CircleCIProvider::detect()
|| GitHubActionsProvider::detect()
|| GitLabCIProvider::detect()
}

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/run_environment/mod.rs
Line: 58-60

Comment:
**CircleCI bypasses memory tuning**

When a memory-mode run executes on CircleCI, `is_ci_environment()` returns false because it omits the supported CircleCI provider, causing all THP, sysctl, swap, and page-cache stabilization to be skipped.

```suggestion
pub fn is_ci_environment() -> bool {
    BuildkiteProvider::detect()
        || CircleCIProvider::detect()
        || GitHubActionsProvider::detect()
        || GitLabCIProvider::detect()
}
```

**Knowledge Base Used:**
- [Executor: benchmark run orchestration](https://app.greptile.com/codspeed/-/custom-context/knowledge-base/codspeedhq/codspeed/-/docs/executor.md)
- [Run environment detection and providers](https://app.greptile.com/codspeed/-/custom-context/knowledge-base/codspeedhq/codspeed/-/docs/run-environment.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex

Comment on lines +145 to +148
if let Err(error) = run_with_sudo("swapoff", ["-a"]) {
warn!("Failed to disable swap: {error}");
return Vec::new();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Failed swapoff loses restoration state

If swapoff -a disables one swap entry and then fails on another, this branch discards the previously captured entry list, so Drop never re-enables the entry already disabled and leaves the host partially swap-disabled.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/executor/memory/tunables.rs
Line: 145-148

Comment:
**Failed swapoff loses restoration state**

If `swapoff -a` disables one swap entry and then fails on another, this branch discards the previously captured entry list, so `Drop` never re-enables the entry already disabled and leaves the host partially swap-disabled.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Comment thread src/executor/memory/tunables.rs
/// Drops the page cache. Nothing to restore: the node is a write-only
/// trigger and the kernel refills the cache on demand.
fn drop_page_cache() {
nix::unistd::sync();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this call here? Could benefit from a comment

pub fn apply() -> Self {
if !crate::run_environment::is_ci_environment() {
debug!("Not running in CI, skipping kernel memory tunables");
return Self::default();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's better to return an option here rather than relying on the default satisfies the condition of the drop guard. This way if we dont do anything, we are SURE we'll not run any command when dropping the tunables guard

Comment on lines +1 to +6
//! Kernel knobs that stabilise memory measurements: transparent huge pages,
//! compaction/swap/NUMA-balancing sysctls, swap and the page cache.
//!
//! [`MemoryTunables`] captures the previous value of every knob it changes and
//! restores it on drop, so a host that only looks like CI — `CI=true` inside a
//! container sharing the host's non-namespaced knobs, say — is left as it was.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we have some traceability and documentation about these tuneables?

Ideally, each should be individually tested for its variance effect. This is to make sure we are not vibe-disabling swap because opus/fable felt like it was a good idea.

/// Applies the knobs on a best-effort basis: a knob that cannot be set is
/// warned about, never fatal.
pub fn apply() -> Self {
if !crate::run_environment::is_ci_environment() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We are not doing this kind of guard for walltime, could we unify behavior? This feels very ad-hoc for now

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.

2 participants