• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aio-libs / multidict / 36270484888
87%

Build:
DEFAULT BRANCH: master
Ran 26 Sep 2026 08:43PM UTC
Jobs 1
Files 44
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

26 Sep 2026 08:43PM UTC coverage: 87.16% (+0.05%) from 87.113%
36270484888

push

github

web-flow
Give each multidict its own block of versions on free-threaded builds (#1605)

<!-- Thank you for your contribution! -->

## What do these changes do?

On free-threaded builds every mutation took its version from a
thread-local counter (#1587, #1599). In a shared object that is a
`__tls_get_addr()` call per mutation, about 23 instructions where the
old `lock xadd` was 4. That call accounted for nearly all of the
`ctor_small` regression (+18.6 Ir per inserted pair) and 18 of the 26 Ir
`pop` lost.

Each multidict now owns a block of 256 versions, and `md->version` is
the cursor into it, advanced under the multidict's own critical section.
Thread-local storage is touched only when a multidict needs a new block:
when it is created, and then after 255 mutations. Each thread still
reserves a batch from the process-wide counter, now 65536 versions (256
blocks) at a time. A block's base is never handed out. That keeps a new
multidict (version 0) and a spent block distinguishable with one mask
test, and versions stay unique across multidicts. `store_version(md,
next_version(md->state))` became `bump_version(md)`. On the GIL build
that is still `++state->global_version`, and `tools/codegen_diff.py`
reports 0 of 230 functions changed there.

Callgrind `ir_per_op`, `multidict_c`, free-threaded 3.14.7t, against
2f84475 (rows that moved by more than 0.1%):

| operation | before | after | |
| --- | --- | --- | --- |
| `cls(items)` | 69893.4 | 66265.3 | -5.2% |
| `cls(items)`, 20 items | 7135.6 | 6765.0 | -5.2% |
| `cls()` | 878.3 | 888.3 | +1.1% |
| `d.copy()`, 20 items | 2763.8 | 2744.8 | -0.7% |
| `d[key] = v`, existing key | 505.1 | 495.1 | -2.0% |
| `d[key] = v`, new key | 610.5 | 597.8 | -2.1% |
| `d.setdefault(key, v)`, new key | 730.1 | 711.4 | -2.6% |
| `del d[key]` | 408.6 | 392.5 | -3.9% |
| `d.pop(key)` | 537.8 | 519.8 | -3.3% |
| `d.popitem()` | 664.7 | 646.6 | -2.7% |
| `d.update(other)` | 44708.0 | 44514.0 | -0.4% |
| `d.clear()` | 8977.6 | 8957.0 | -0... (continued)

781 of 1562 branches covered (50.0%)

Branch coverage included in aggregate %.

38 of 38 new or added lines in 1 file covered. (100.0%)

8261 of 8812 relevant lines covered (93.75%)

1.87 hits per line

Jobs
ID Job ID Ran Files Coverage
1 MyPy - 36270484888.1 26 Sep 2026 08:43PM UTC 88
87.16
GitHub Action Run
Source Files on build 36270484888
  • Tree
  • List 44
  • Changed 3
  • Source Changed 1
  • Coverage Changed 3
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • Github Actions Build #36270484888
  • 98b367fb on github
  • Prev Build on master (#36270462946)
  • Next Build on master (#36272009708)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc