The Fastest Python Struct?

I’ve found myself reflexively using NamedTuple everywhere in my projects, and suddenly realized that I’d been assuming some performance characteristics somewhat religously. Now, I’m not someone that invests in performance twiddling, but I do care about python startup times, so I decided to setup a benchmark (with assistance from claude-opus-4-8 ) for just that: a comparison of struct/record-like types.

Measurements include module cost (import time), type cost (import time), instance cost, and memory cost. I included cold (needs compile), warm (bytecode available), and mypyc. Implementations tested include “native slots”, manual record (based on record-type), NamedTuple, dataclass, record-type, record-type (C impl), attrs, and msgspec.

The post is on my blog, LMK what you think!

Cheers,
JPH

You might be pleased to hear that there are some changes coming that should improve things and some that I hope we can get in to improve things further, at least on dataclasses’ end.

In Python 3.15 dataclasses now imports a number of things lazily and avoids using them if possible, so the module import should come down (mostly it no longer imports inspect unless you create a class with no docstring and attempt to actually look at the __doc__).

I have a plan to make dataclasses class construction faster by avoiding the generation of unused methods - Faster (lazier) dataclasses - #18 by DavidCEllis - if I get this in it should bring down the type creation to be faster than namedtuple (from collections) and type creation including __init__ to be close to NamedTuple (from typing).

I think there are some small tweaks we can make to make creating frozen dataclass instances faster too. Currently both non-slotted and slotted classes use object.__setattr__ for assignment. Non-slotted classes could get a performance boost from writing directly to the instance __dict__ instead. attrs does this already, but defaults to making slotted classes. attrs slotted classes also get object.__setattr__ as a bound method on the instance which may explain the slight difference in performance that you see.

No timings for just a plain straight tuple?

There’s no way to avoid creating the plain straight tuple type. How would you measure that?

I suppose that I could measure the instance cost of an anonymous tuple. In fact, I might do that in another benchmark, since I tend to use them instead of lists, assuming they’re more efficient with no evidence.

This benchmark was narrowly focused on statically typed structs.

Great work! It’s amazing to think that lazy imports will speed up the standard library.

I would like to see the performance of collections.namedtuple to see how much (if any) overhead is added by Typing.NamedTuple, to see if the easier use and readability of NamedTuple is worth it.

I’ll try to add that on the next update. It will be informative to reveal the cost of NamedTuple’s static typing by comparing it directly to namedtuple.

I really enjoy seeing data like this. I used to work in a strange proprietary language wherein object variable lookup was a major bottleneck, and I once refactored some of the hottest code in that application to use lists and dicts instead of objects, with the lists keyed by preprocessor defines. I’m glad that nothing like that seems to be relevant in python!