Skip to content

Instantly share code, notes, and snippets.

@wolph
Last active February 14, 2023 21:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wolph/02fae0b20b914354734aaac01c06d23b to your computer and use it in GitHub Desktop.
Save wolph/02fae0b20b914354734aaac01c06d23b to your computer and use it in GitHub Desktop.
Benchmark namedtuple vs dataclass vs dict
import sys
import enum
import math
import random
import timeit
import typing
import dataclasses
import collections
repeat = 5
number = 1000
N = 5000
class PointTuple(typing.NamedTuple):
x: int
y: int
z: int
@dataclasses.dataclass
class PointDataclass:
x: int
y: int
z: int
@dataclasses.dataclass(slots=True)
class PointDataclassSlots:
x: int
y: int
z: int
class PointObject:
__slots__ = 'x', 'y', 'z'
x: int
y: int
z: int
def test_namedtuple_attr():
point = PointTuple(1234, 5678, 9012)
for i in range(N):
x, y, z = point.x, point.y, point.z
def test_namedtuple_index():
point = PointTuple(1234, 5678, 9012)
for i in range(N):
x, y, z = point
def test_namedtuple_unpack():
point = PointTuple(1234, 5678, 9012)
for i in range(N):
x, *y = point
def test_dataclass():
point = PointDataclass(1234, 5678, 9012)
for i in range(N):
x, y, z = point.x, point.y, point.z
def test_dataclass_slots():
point = PointDataclassSlots(1234, 5678, 9012)
for i in range(N):
x, y, z = point.x, point.y, point.z
def test_dict():
point = dict(x=1234, y=5678, z=9012)
for i in range(N):
x, y, z = point['x'], point['y'], point['z']
def test_slots():
point = PointObject()
point.x = 1234
point.y = 5678
point.z = 9012
for i in range(N):
x, y, z = point.x, point.y, point.z
class PointEnum(enum.Enum):
x = 1
y = 2
z = 3
def test_enum_attr():
point = PointEnum
for i in range(N):
x, y, z = point.x, point.y, point.z
def test_enum_call():
point = PointEnum
for i in range(N):
x, y, z = point(1), point(2), point(3)
def test_enum_item():
point = PointEnum
for i in range(N):
x, y, z = point['x'], point['y'], point['z']
if __name__ == '__main__':
tests = [
test_namedtuple_attr,
test_namedtuple_index,
test_namedtuple_unpack,
test_dataclass,
test_dataclass_slots,
test_dict,
test_slots,
test_enum_attr,
test_enum_call,
test_enum_item,
]
print(f'Running tests {repeat} times with {number} calls.')
print(f'Using {N} iterations in the loop')
results = collections.defaultdict(lambda: math.inf)
for i in range(repeat):
# Shuffling tests to prevent skewed results due to CPU boosting or
# thermal throttling
random.shuffle(tests)
print(f'Run {i}:', end=' ')
for t in tests:
name = t.__name__
print(name, end=', ')
sys.stdout.flush()
timer = timeit.Timer(f'{name}()', f'from __main__ import {name}')
results[name] = min(results[name], timer.timeit(number))
print()
for name, result in sorted(results.items(), key=lambda x: x[::-1]):
print(f'{name:30} {result:.3f}s')
@DOWRIGHTTV
Copy link

I got here from stack overflow: https://stackoverflow.com/questions/2646157/what-is-the-fastest-to-access-struct-like-object-in-python

I like this test, but I agree with the poster about x, y, z = point[0], point[1], point[2], though I didn't really notice a significant difference running this on python3.9. Also, I don't think "test_namedtuple_unpack" needs to be included because that is more on the code that handles the *args.

I personally wanted to see how enums faired, so i added them. Here is the code if you want to include it.
side note: they did not fair well.

import enum

class PointEnum(enum.Enum):
    x = 1
    y = 2
    z = 3


def test_enum_attr():
    point = PointEnum

    for i in range(N):
        x, y, z = point.x, point.y, point.z

def test_enum_call():
    point = PointEnum

    for i in range(N):
        x, y, z = point(1), point(2), point(3)

def test_enum_item():
    point = PointEnum

    for i in range(N):
        x, y, z = point['x'], point['y'], point['z']

def test_namedtuple_index():
    point = PointTuple(1234, 5678, 9012)

    for i in range(N):
        x, y, z = point[0], point[1], point[2]

if __name__ == '__main__':
    tests = [
        test_enum_attr,
        test_enum_call,
        test_enum_item,
        test_namedtuple_attr,
        test_namedtuple_index,
        # test_namedtuple_unpack,
        test_dataclass,
        test_dict,
        test_slots,
    ]
    ...

Here is my py3.9 run via pycharm on win11, amd 5 ryzen 2600

Running tests 5 times with 1000 calls.
Using 5000 iterations in the loop
Run 0: test_dataclass, test_namedtuple_index, test_enum_attr, test_slots, test_enum_call, test_enum_item, test_namedtuple_attr, test_dict,
Run 1: test_namedtuple_index, test_enum_item, test_dataclass, test_slots, test_enum_attr, test_dict, test_enum_call, test_namedtuple_attr,
Run 2: test_dataclass, test_enum_item, test_enum_call, test_enum_attr, test_namedtuple_index, test_dict, test_slots, test_namedtuple_attr,
Run 3: test_namedtuple_attr, test_dataclass, test_enum_call, test_dict, test_namedtuple_index, test_slots, test_enum_item, test_enum_attr,
Run 4: test_enum_item, test_dict, test_namedtuple_index, test_dataclass, test_enum_call, test_slots, test_namedtuple_attr, test_enum_attr,
test_dataclass 0.35615059999999943
test_namedtuple_index 0.4273814000000016
test_namedtuple_attr 0.43110919999999453
test_slots 0.4442202999999978
test_dict 0.5744749999999996
test_enum_attr 1.4315308999999985
test_enum_item 2.6604619000000014
test_enum_call 7.318292999999997

@wolph
Copy link
Author

wolph commented Jan 7, 2023

Also, I don't think "test_namedtuple_unpack" needs to be included because that is more on the code that handles the *args.

While I agree, it's bound to be a question/comment if I don't ;)
And sometimes these results can be surprising so it might make a difference somehow

I personally wanted to see how enums faired, so i added them. Here is the code if you want to include it. side note: they did not fair well.

Very cool! I'll rerun the tests on Python 3.11 and update the benchmark and the answer :)

@wolph
Copy link
Author

wolph commented Jan 7, 2023

I've also added a dataclass with slots with this version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment