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)