Skip to content

Skip the reference pool mutex on attach when no decrefs are pending - #6200

Open
tobni wants to merge 5 commits into
PyO3:mainfrom
tobni:pool-dirty-flag
Open

Skip the reference pool mutex on attach when no decrefs are pending#6200
tobni wants to merge 5 commits into
PyO3:mainfrom
tobni:pool-dirty-flag

Conversation

@tobni

@tobni tobni commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Closes #6199.

@tobni
tobni force-pushed the pool-dirty-flag branch 2 times, most recently from 1fa73e7 to bf64a85 Compare July 12, 2026 21:42
@codspeed-hq

codspeed-hq Bot commented Jul 12, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 140 untouched benchmarks
🆕 7 new benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
🆕 Simulation empty_pool_attach N/A 2.7 µs N/A
🆕 WallTime nested_attach_scaling/empty_pool[1] N/A 6 ns N/A
🆕 WallTime nested_attach_scaling/empty_pool[2] N/A 6 ns N/A
🆕 WallTime nested_attach_scaling/empty_pool[4] N/A 10 ns N/A
🆕 WallTime nested_attach_scaling/sparse_pool[1] N/A 6 ns N/A
🆕 WallTime nested_attach_scaling/sparse_pool[2] N/A 7 ns N/A
🆕 WallTime nested_attach_scaling/sparse_pool[4] N/A 10 ns N/A

Comparing tobni:pool-dirty-flag (2e7b17b) with main (1ed13a0)

Open in CodSpeed

@ngoldbaum

Copy link
Copy Markdown
Contributor

The 128 bit conversion benchmarks are the failure can be ignored.

@ngoldbaum

Copy link
Copy Markdown
Contributor

FWIW having a lock-free fast path for a mutex makes sense to me as a general thing to help scaling but I haven't thought deeply about the implications here.

@tobni

tobni commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

FWIW having a lock-free fast path for a mutex makes sense to me as a general thing to help scaling but I haven't thought deeply about the implications here.

Good to hear!

It is the de-facto bottleneck for pants concurrency model, so I am keen on this issue being resolved.

@davidhewitt davidhewitt 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.

Thanks for the PR! Is it possible to build a benchmark which demonstrates the pathological case here? As per the issue we've flip-flopped on this a fair bit, I'm definitely open to having the complexity if we can prove it's worth it.

Comment thread src/internal/state.rs
if !self.dirty.load(Ordering::Acquire) {
return;
}
self.dirty.store(false, Ordering::Relaxed);

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.

Should this pair use compare_exchange?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that compare_exchange would re-introduce the contention point of every attach becoming a writer. I think it is benign that threads CAN fall through to pending_decrefs turning out empty.

TLDR;
No, I dont think it should.

@tobni

tobni commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the PR! Is it possible to build a benchmark which demonstrates the pathological case here? As per the issue we've flip-flopped on this a fair bit, I'm definitely open to having the complexity if we can prove it's worth it.

I am struggling a bit to build a benchmark that codspeed can execute that proves contention is reduced with this change. Would a bench that measures the fastpath be sufficient?

Edit: I have become more familiar with codspeed and will push a suggestion of walltime benchmark. This might be a larger maintenance burden than this change warrants, so please scrutinize 8530cd7.

@tobni
tobni force-pushed the pool-dirty-flag branch from bf64a85 to b3d3014 Compare July 25, 2026 13:46
@tobni

tobni commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

@davidhewitt PTAL

@tobni
tobni force-pushed the pool-dirty-flag branch from b865488 to b0c1e85 Compare July 25, 2026 14:08
@tobni

tobni commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

These are aggregate results I net using the pushed benchmark locally. I have an 8 pysical-core machine.
The 2 cores is to simulate the CI runner. The unit is total attaches per second

empty_pool:

┌────────────────────┬───────┬────────┬───────┐
│      threads       │ main  │ branch │ ratio │
├────────────────────┼───────┼────────┼───────┤
│ 16 cores           │       │        │       │
├────────────────────┼───────┼────────┼───────┤
│ 1                  │ 104.0 │ 374.0  │ 3.6×  │
├────────────────────┼───────┼────────┼───────┤
│ 2                  │ 35.6  │ 725.9  │ 20.4× │
├────────────────────┼───────┼────────┼───────┤
│ 4                  │ 26.3  │ 1269.1 │ 48.3× │
├────────────────────┼───────┼────────┼───────┤
│ 2 cores            │       │        │       │
├────────────────────┼───────┼────────┼───────┤
│ 1                  │ 103.9 │ 373.1  │ 3.6×  │
├────────────────────┼───────┼────────┼───────┤
│ 2                  │ 35.6  │ 553.0  │ 15.5× │
├────────────────────┼───────┼────────┼───────┤
│ 4                  │ 35.0  │ 606.0  │ 17.3× │
└────────────────────┴───────┴────────┴───────┘

sparse_pool is ~ equivalent

@alex

alex commented Jul 25, 2026

Copy link
Copy Markdown
Member

@davidhewitt I feel like we used to have this, and then it got removed at some point, do you remember the history? Am I mixing this up with something else?

@tobni

tobni commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

@davidhewitt I feel like we used to have this, and then it got removed at some point, do you remember the history? Am I mixing this up with something else?

I did some digging and documented my archeology findings in #6199. I believe that illuminates the history.

Edit:
It is specifically this change #3250 that removed the dirty boolean prior. I note that the implementation that was removed was using swap, a read-modify-write instruction (i.e every attach is a write). I'm not familiar with the 2023 code, but it looks to have tracked increfs as well as decrefs.

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.

Reference pool mutex is taken on every attach which serializes multithreaded embedders

4 participants