jcrist · GitHub

Our Struct.__init__ was already pretty optimized (and simple), but had some non-ideal behavior if kwargs were passed in.

Previously __init__ would run in ~n_fields * n_keywords_passed_in. By rearranging some code, __init__ runs in ~n_fields.

Benchmarks

Using this struct definition:

import msgspec
class Bench(msgspec.Struct):
    field_a: int | None = None
    field_b: int | None = None
    field_c: int | None = None
    field_d: int | None = None
    field_e: int | None = None
    field_f: int | None = None
    field_g: int | None = None
    field_h: int | None = None
    field_i: int | None = None
    field_j: int | None = None

Previously

In [1]: %timeit Bench()
93.9 ns ± 0.794 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [2]: %timeit Bench(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
79 ns ± 0.188 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [3]: %timeit Bench(0, 1, 2, 3, 4, field_f=5, field_g=6, field_h=7, field_i=8, field_j=9)
247 ns ± 0.75 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [4]: %timeit Bench(field_f=5, field_g=6, field_h=7, field_i=8, field_j=9)
250 ns ± 1.66 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

This PR

In [1]: %timeit Bench()
85.9 ns ± 0.158 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [2]: %timeit Bench(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
68.6 ns ± 0.34 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [3]: %timeit Bench(0, 1, 2, 3, 4, field_f=5, field_g=6, field_h=7, field_i=8, field_j=9)
78.3 ns ± 1.84 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [4]: %timeit Bench(field_f=5, field_g=6, field_h=7, field_i=8, field_j=9)
96.5 ns ± 0.253 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

Standard Python Classes

In [1]: %timeit Bench()
150 ns ± 0.167 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [2]: %timeit Bench(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
190 ns ± 0.446 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [3]: %timeit Bench(0, 1, 2, 3, 4, field_f=5, field_g=6, field_h=7, field_i=8, field_j=9)
356 ns ± 0.283 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [4]: %timeit Bench(field_f=5, field_g=6, field_h=7, field_i=8, field_j=9)
325 ns ± 1.87 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Read the original on github.com ↗