Skip to content

perf: avoid hot-path atomic write when circuit state is unchanged#30

Merged
costela merged 1 commit into
mainfrom
perf/avoid-hotpath-atomic-write
Jul 15, 2026
Merged

perf: avoid hot-path atomic write when circuit state is unchanged#30
costela merged 1 commit into
mainfrom
perf/avoid-hotpath-atomic-write

Conversation

@costela

@costela costela commented Jun 26, 2026

Copy link
Copy Markdown
Member

What

Guard the openedAt mutation in stateObserver.Observe with a Load so a steady-state circuit doesn't write to shared state on every call.

Why

SlidingWindowBreaker.observe returns stateChangeClose on every below-threshold call, so a healthy closed circuit ran openedAt.Store(0) on every call — an unconditional atomic write to a cache line that's already 0. Under concurrency that bounces the line between cores (MESI invalidation), reintroducing exactly the cross-core contention the lock-free design exists to avoid — for a write that changes nothing.

How

  • stateChangeOpen → only open() if openedAt.Load() == 0.
  • stateChangeClose → only close() if openedAt.Load() != 0.

A Load keeps the cache line shared across cores; the steady-state happy path becomes read-only.

Results

Micro-benchmarks isolating open()/close() on an unchanged circuit show a large win (the Load keeps the line shared instead of forcing exclusive ownership per call; the serial open() win is mostly from skipping time.Now()). The end-to-end Do path shows no measurable change — at ~565 ns/op it is dominated by the per-call goroutine and 5 allocations, so the ~10–25 ns saved is below the noise floor. This is contention hygiene, not a throughput improvement.

benchstat, 10 runs, AMD Ryzen AI 9 HX 370 (24 threads); Circuit_* are throwaway micro-benchmarks calling open()/close() directly:

                                        │   old.bench    │               new.bench                │
                                        │     sec/op     │     sec/op      vs base                │
Hoglet_Do_EWMA-24                            565.5n ± 2%     574.0n ±  2%        ~ (p=0.363 n=10)
Hoglet_Do_SlidingWindow-24                   564.8n ± 1%     567.8n ±  3%        ~ (p=0.218 n=10)
Circuit_close_alreadyClosed_parallel-24   22.74000n ± 4%   0.05736n ±  6%  -99.75% (p=0.000 n=10)
Circuit_close_alreadyClosed_serial-24      10.0450n ± 2%    0.5050n ±  6%  -94.97% (p=0.000 n=10)
Circuit_open_alreadyOpen_parallel-24       28.2650n ± 1%    0.2400n ±  5%  -99.15% (p=0.000 n=10)
Circuit_open_alreadyOpen_serial-24         102.500n ± 2%     2.502n ± 22%  -97.56% (p=0.000 n=10)

B/op and allocs/op are unchanged. (An earlier version of this description claimed −31…−43% on the end-to-end benchmarks; that did not reproduce.)

🤖 Generated with Claude Code

@costela
costela marked this pull request as ready for review June 26, 2026 19:06
@costela
costela requested a review from Copilot June 26, 2026 19:06

Copilot AI left a comment

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.

Pull request overview

This PR reduces cross-core contention in the circuit breaker hot path by avoiding unnecessary atomic writes to Circuit.openedAt when the circuit state is already in the desired steady state (closed or open).

Changes:

  • Guard stateObserver.Observe’s open()/close() calls with openedAt.Load() checks to skip redundant atomic writes.
  • Keep the steady-state closed-circuit path read-only when SlidingWindowBreaker.observe repeatedly returns stateChangeClose.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread hoglet.go Outdated
@costela
costela force-pushed the perf/avoid-hotpath-atomic-write branch from f23859d to 53b65f2 Compare July 9, 2026 14:23
SlidingWindowBreaker.observe returns stateChangeClose on every call below
threshold, so a healthy closed circuit executed openedAt.Store(0) on every
single call - an unconditional atomic write to one shared cache line that is
already 0. Under concurrency this bounces the cache line between cores (MESI
invalidation), reintroducing the cross-core contention the lock-free design
exists to avoid, for a write that changes nothing.

Guard both transitions with a Load first: only open() when currently closed
and only close() when currently open. A Load keeps the cache line shared
across cores; the steady-state happy path becomes read-only. open()'s
CompareAndSwap already no-ops when already open, but the Load avoids even
issuing the read-modify-write.

Benchstat (parallel benchmarks): ~9-43% faster, scaling with core count
exactly as the contention hypothesis predicts; single-threaded SlidingWindow
shows no change (no contention to relieve).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@costela
costela force-pushed the perf/avoid-hotpath-atomic-write branch from 53b65f2 to e03a571 Compare July 9, 2026 14:27

@obitech obitech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@costela
costela merged commit 1f7cfe0 into main Jul 15, 2026
2 checks passed
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.

3 participants