GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-127295: ctypes: Switch field accessors to fixed-width integers by encukou · Pull Request #127297 · python/cpython

Conversation

@encukou

@encukou encukou commented Nov 26, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

See the issue.

This refactoring has a miniscule but consistent performance benefit (1.01x geometric mean; edit: 46 cases slower & 415 faster). See my bench script & results. Thanks @vstinner for pyperf and instructions for isolating CPU cores!

For the repetitive parts, this uses a combination of macros and Argument Clinic for code generation (inline, so one can easily inspect the results). This is the most readable/maintainable of various approaches I tried.
(No, I'm not a fan of the giant macros, but it beats both code generated from f-strings that lack syntax highlighting, and external multiple-include files. Oh, and the /////////// lines make it easy to see misplaced backslashes.)

Several related changes are included:

  • Pass a meaningful size argument, rather than zero, to non-bitfield accessors. (This would be great to have for size-generic functions, and it should be a private implementation detail. I want to do this now and get the asserts in the wild, to see if I missed someone who's relying on the detail.)
  • Use a switch in _ctypes_get_fielddesc, rather than a linear search. (This requires that there are now several boring chunks with a line for each of the format codes, sbBcdCEFgfhHiIlLqQPzuUZXvO, but Argument Clinic makes this bearable.)
  • Rearrange struct fielddesc to move all the accessors together, making code generation a bit easier
  • Motley consistency improvements, like changing BSTR in function names to its code char X, to match the other accessors
  • Issue: ctypes: Switch field accessors to fixed-width integers #127295

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
encukou added 3 commits November 26, 2024 14:18
@encukou
Consistently pass the size to getfunc/setfunc
eeb0d4e
@encukou
Switch to fixed-width integers
e33c929
Replace formattable by a switch.

Generate some repetitive parts of handling individual C types.
@encukou
Remove comment for obsolete idea
a11c0d9
@bedevere-app bedevere-app Bot mentioned this pull request Nov 26, 2024
ctypes: Switch field accessors to fixed-width integers #127295
Closed
@bedevere-app bedevere-app Bot added the awaiting core review label Nov 26, 2024
@encukou encukou added the skip news label Nov 26, 2024
@encukou
encukou marked this pull request as draft November 26, 2024 15:03
@bedevere-app bedevere-app Bot removed the awaiting core review label Nov 26, 2024
@encukou

encukou commented Nov 26, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

(Yes, this is wrong as-is; hopefully due to a silly mistake on my part.)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

encukou added 6 commits November 26, 2024 16:10
@encukou
Fix the size argument
c69b65f
@encukou
Re-run Clinic
eb8cf98
@encukou
Regen Clinic
b9ed727
@encukou
Specify signedness of the FFI types, don't match the C types
7cbe57c
@encukou
Avoid compiler warnings
144a3b0
@encukou
Silence the GCC warning in a more local way. It's a GCC bug.
fcae66b
@encukou
encukou marked this pull request as ready for review November 27, 2024 08:42
@bedevere-app bedevere-app Bot added the awaiting core review label Nov 27, 2024
@encukou encukou added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Nov 27, 2024
@bedevere-bot

bedevere-bot commented Nov 27, 2024

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @encukou for commit fcae66b 🤖

If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Nov 27, 2024
@encukou

encukou commented Dec 6, 2024

Copy link
Copy Markdown
Member Author

@ZeroIntensity @picnixz @serhiy-storchaka, would one of you be interested in looking at these changes?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@serhiy-storchaka
serhiy-storchaka self-requested a review December 6, 2024 10:40
@picnixz

picnixz commented Dec 6, 2024

Copy link
Copy Markdown
Member

I'll look into it but let me first clean-up my backlog (so probably in a few hours or tomorrow)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz
picnixz self-requested a review December 6, 2024 10:40
@encukou

encukou commented Dec 6, 2024

Copy link
Copy Markdown
Member Author

No rush :)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ZeroIntensity
ZeroIntensity self-requested a review December 6, 2024 12:50
@ZeroIntensity

ZeroIntensity commented Dec 6, 2024

Copy link
Copy Markdown
Member

I'll take a look later today :)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

picnixz
picnixz reviewed Dec 6, 2024
View reviewed changes

@picnixz picnixz left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

A first round of review.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

encukou and others added 2 commits December 6, 2024 15:44
@encukou @picnixz
Apply suggestions from code review
05ba989
Co-authored-by: Bénédikt Tran <[email protected]>
@encukou
Add parens around macro argument
7c08d60
encukou added 3 commits December 6, 2024 15:45
@encukou
Fix refcounting for _CTYPES_DEBUG_KEEP builds
50c82be
@encukou
Consistency in comments. Why not.
7375c09
@encukou
Merge in the main branch
6e8a4de
ZeroIntensity
ZeroIntensity reviewed Dec 7, 2024
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

This mostly LGTM. My main concern is that this adds some new thread safety issues with global variables, but knowing ctypes, it probably didn't work before anyway.

cc @skirpichev, you might be interested in looking at the integer-related parts of this PR.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

encukou added 2 commits December 9, 2024 12:27
@encukou
Include <stdbool.h> & use bool rather than _Bool
870a038
@encukou
Use a mutex around _ctypes_init_fielddesc
1b41ace
@encukou encukou mentioned this pull request Dec 9, 2024
gh-126937: ctypes: fix TypeError when a field's size is >65535 bytes #126938
Merged
@skirpichev
skirpichev self-requested a review December 10, 2024 00:09
@encukou encukou mentioned this pull request Dec 11, 2024
[3.13] gh-126937: ctypes: add test for maximum size of a struct field (GH-126938) #127825
Merged
ZeroIntensity
ZeroIntensity reviewed Dec 12, 2024
View reviewed changes
Comment thread Modules/_ctypes/cfield.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_ctypes/cfield.c
static PyObject *
g_set(void *ptr, PyObject *value, Py_ssize_t size)
{
assert(NUM_BITS(size) || (size == sizeof(long double)));

@ZeroIntensity ZeroIntensity Dec 10, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

This is repeated a lot; is it worth making it its own macro?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@encukou encukou Dec 13, 2024

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I prefer this for now.
One advantage of writing this out is that it's much clearer if you land on this line in a debugger. With this one line, the trade-off isn't worth it.

(I'll be the first to admit that the macros this PR adds are a pain to debug, but, IMO they also remove enough duplication to be worth it.)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
encukou added 2 commits December 13, 2024 11:55
@encukou
Merge in the main branch
07d7ded
@encukou
Nitpick: Use true/false for bool
34098c3
@encukou

encukou commented Dec 13, 2024

Copy link
Copy Markdown
Member Author

@serhiy-storchaka, should I wait for your review?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity
ZeroIntensity approved these changes Dec 13, 2024
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I did one final pass and I'm happy to say that this LGTM.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@encukou

encukou commented Dec 16, 2024

Copy link
Copy Markdown
Member Author

Thank you!

If there are no objections, I plan to merge on Wednesday, and work on the next PR in this area.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

skirpichev
skirpichev reviewed Dec 17, 2024
View reviewed changes
Comment thread Modules/_ctypes/cfield.c
Comment on lines +389 to +396
if (PyLong_Check(value) \
&& PyUnstable_Long_IsCompact((PyLongObject *)value)) \
{ \
val = (CTYPE)PyUnstable_Long_CompactValue( \
(PyLongObject *)value); \
} \
else { \
Py_ssize_t res = PyLong_AsNativeBytes( \

@skirpichev skirpichev Dec 17, 2024

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

PyLong_AsNativeBytes() already has a quick path for compact values. Did you check performance impact without first if statement?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@encukou

encukou commented Dec 18, 2024

Copy link
Copy Markdown
Member Author

Yes, without the if it's slightly but measurably slower. With the benchmarks in the OP: Geometric mean: 1.02x slower; 526 slower cases, 87 faster, 635 not significant.

(Perhaps that would be worth the simplification, but that's for another PR & discussion.)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou
Avoid unaligned load
5155293
@encukou encukou added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Dec 18, 2024
@bedevere-bot

bedevere-bot commented Dec 18, 2024

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @encukou for commit 5155293 🤖

If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Dec 18, 2024
@encukou
Merge branch 'main' into ctypes-fixint
821a7b8
@encukou
encukou merged commit 78ffba4 into python:main Dec 20, 2024
@bedevere-app bedevere-app Bot removed the awaiting core review label Dec 20, 2024
@encukou
encukou deleted the ctypes-fixint branch December 20, 2024 13:28
@encukou

encukou commented Dec 20, 2024

Copy link
Copy Markdown
Member Author

Thank you for the reviews!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

srinivasreddy pushed a commit to srinivasreddy/cpython that referenced this pull request Dec 23, 2024
@encukou @picnixz @srinivasreddy
pythongh-127295: ctypes: Switch field accessors to fixed-width intege…
6ef3119
…rs (pythonGH-127297)

This should be a pure refactoring, without user-visible behaviour changes.

Before this change, ctypes uses traditional native C types, usually identified 
by [`struct` format characters][struct-chars] when a short (and 
identifier-friendly) name is needed:
- `signed char` (`b`) / `unsigned char` (`B`)
- `short` (`h`) / `unsigned short` (`h`)
- `int` (`i`) / `unsigned int` (`i`)
- `long` (`l`) / `unsigned long` (`l`)
- `long long` (`q`) / `unsigned long long` (`q`)

These map to C99 fixed-width types, which this PR switches to: - 
- `int8_t`/`uint8_t`
- `int16_t`/`uint16_t`
- `int32_t`/`uint32_t`
- `int64_t`/`uint64_t`

The C standard doesn't guarantee that the “traditional” types must map to the 
fixints. But, [`ctypes` currently requires it][swapdefs], so the assumption won't 
break anything.

By “map” I mean that the *size* of the types matches. The *alignment* 
requirements might not. This needs to be kept in mind but is not an issue in 
`ctypes` accessors, which [explicitly handle unaligned memory][memcpy] for the 
integer types.

Note that there are 5 “traditional” C type sizes, but 4 fixed-width ones. Two of 
the former are functionally identical to one another; which ones they are is 
platform-specific (e.g. `int`==`long`==`int32_t`.) This means that one of the 
[current][current-impls-1] [implementations][current-impls-2] is redundant on 
any given platform.


The fixint types are parametrized by the number of bytes/bits, and one bit for 
signedness. This makes it easier to autogenerate code for them or to write 
generic macros (though generic API like 
[`PyLong_AsNativeBytes`][PyLong_AsNativeBytes] is problematic for performance 
reasons -- especially compared to a `memcpy` with compile-time-constant size).

When one has a *different* integer type, determining the corresponding fixint  
means a `sizeof` and signedness check. This is easier and more robust than the 
current implementations (see [`wchar_t`][sizeof-wchar_t] or 
[`_Bool`][sizeof-bool]).


[swapdefs]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L420-L444
[struct-chars]: https://docs.python.org/3/library/struct.html#format-characters
[current-impls-1]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L470-L653
[current-impls-2]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L703-L944
[memcpy]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L613
[PyLong_AsNativeBytes]: https://docs.python.org/3/c-api/long.html#c.PyLong_AsNativeBytes
[sizeof-wchar_t]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1547-L1555
[sizeof-bool]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1562-L1572


Co-authored-by: Bénédikt Tran <[email protected]>
srinivasreddy pushed a commit to srinivasreddy/cpython that referenced this pull request Jan 8, 2025
@encukou @picnixz @srinivasreddy
pythongh-127295: ctypes: Switch field accessors to fixed-width intege…
fcac658
…rs (pythonGH-127297)

This should be a pure refactoring, without user-visible behaviour changes.

Before this change, ctypes uses traditional native C types, usually identified 
by [`struct` format characters][struct-chars] when a short (and 
identifier-friendly) name is needed:
- `signed char` (`b`) / `unsigned char` (`B`)
- `short` (`h`) / `unsigned short` (`h`)
- `int` (`i`) / `unsigned int` (`i`)
- `long` (`l`) / `unsigned long` (`l`)
- `long long` (`q`) / `unsigned long long` (`q`)

These map to C99 fixed-width types, which this PR switches to: - 
- `int8_t`/`uint8_t`
- `int16_t`/`uint16_t`
- `int32_t`/`uint32_t`
- `int64_t`/`uint64_t`

The C standard doesn't guarantee that the “traditional” types must map to the 
fixints. But, [`ctypes` currently requires it][swapdefs], so the assumption won't 
break anything.

By “map” I mean that the *size* of the types matches. The *alignment* 
requirements might not. This needs to be kept in mind but is not an issue in 
`ctypes` accessors, which [explicitly handle unaligned memory][memcpy] for the 
integer types.

Note that there are 5 “traditional” C type sizes, but 4 fixed-width ones. Two of 
the former are functionally identical to one another; which ones they are is 
platform-specific (e.g. `int`==`long`==`int32_t`.) This means that one of the 
[current][current-impls-1] [implementations][current-impls-2] is redundant on 
any given platform.


The fixint types are parametrized by the number of bytes/bits, and one bit for 
signedness. This makes it easier to autogenerate code for them or to write 
generic macros (though generic API like 
[`PyLong_AsNativeBytes`][PyLong_AsNativeBytes] is problematic for performance 
reasons -- especially compared to a `memcpy` with compile-time-constant size).

When one has a *different* integer type, determining the corresponding fixint  
means a `sizeof` and signedness check. This is easier and more robust than the 
current implementations (see [`wchar_t`][sizeof-wchar_t] or 
[`_Bool`][sizeof-bool]).


[swapdefs]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L420-L444
[struct-chars]: https://docs.python.org/3/library/struct.html#format-characters
[current-impls-1]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L470-L653
[current-impls-2]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L703-L944
[memcpy]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L613
[PyLong_AsNativeBytes]: https://docs.python.org/3/c-api/long.html#c.PyLong_AsNativeBytes
[sizeof-wchar_t]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1547-L1555
[sizeof-bool]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1562-L1572


Co-authored-by: Bénédikt Tran <[email protected]>
@matthiasgoergens matthiasgoergens mentioned this pull request Jul 30, 2026
gh-96821: Fix undefined behaviour in _ctypes/cfield.c #96925
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@skirpichev skirpichev skirpichev left review comments
@picnixz picnixz picnixz left review comments
@ZeroIntensity ZeroIntensity ZeroIntensity approved these changes
@serhiy-storchaka serhiy-storchaka Awaiting requested review from serhiy-storchaka

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

skip news

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@encukou @bedevere-bot @picnixz @ZeroIntensity @skirpichev
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.