Stop building floordiv at import time when seeding UDT auto-lift - #610
Open
eriknw wants to merge 1 commit into
Open
Stop building floordiv at import time when seeding UDT auto-lift#610eriknw wants to merge 1 commit into
eriknw wants to merge 1 commit into
Conversation
eriknw
marked this pull request as ready for review
August 4, 2026 16:07
eriknw
force-pushed
the
30-udf-import-cost
branch
3 times, most recently
from
August 5, 2026 03:18
b434a55 to
bebe448
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
from
August 5, 2026 17:44
bebe448 to
924625d
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
from
August 5, 2026 18:03
924625d to
59b2c9e
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
2 times, most recently
from
August 6, 2026 07:59
9fc661a to
82279ee
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
from
August 6, 2026 15:39
82279ee to
7eb2423
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
2 times, most recently
from
August 6, 2026 20:36
4323f4f to
84ca399
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
from
August 6, 2026 20:42
84ca399 to
27b2b3e
Compare
eriknw
force-pushed
the
30-udf-import-cost
branch
from
August 7, 2026 02:48
27b2b3e to
37c456e
Compare
The first access to any operator namespace paid a numba compile it did not need. BinaryOp._initialize ended with a loop over _BUILTIN_UDT_BINARY_OPS that seeded _udt_types/_udt_ops/_custom_dtype via getattr(binary, op_name). floordiv is the one name in that set registered with lazy=True, so the getattr materialized it, compiling every dtype signature on every process start. Seed those three attributes in __init__ instead. That covers every path that creates a BinaryOp (builtin enumeration, the specials in _initialize, delayed UDF materialization, anonymous), so the seeding is not lost, and laziness survives: floordiv still compiles on first use and UDT lift behavior is unchanged. Measured here, fresh process, first `binary.plus` access: ~648 ms before, ~152 ms after (medians of 3 and 5 runs). Treat these as indicative, not benchmark-grade; load average was ~4 on a shared machine throughout, so the absolute numbers move but the ~4x gap does not. This does not make imports free. The ~152 ms that remains is the rest of _initialize plus namespace setup and is untouched here. It also does not change what floordiv costs once you use it; the compile is deferred, not removed. The other four lazily-registered UDFs (rfloordiv, absfirst, abssecond, rpow) were never in _BUILTIN_UDT_BINARY_OPS and so were never force-built by this loop. test_initialize_does_not_build_lazy_udfs covers the invariant in a subprocess, since _initialize has already run by the time any in-process test executes.
eriknw
force-pushed
the
30-udf-import-cost
branch
from
August 7, 2026 05:09
37c456e to
03ff41f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The first access to any operator namespace paid a numba compile it did
not need. BinaryOp._initialize ended with a loop over
_BUILTIN_UDT_BINARY_OPS that seeded _udt_types/_udt_ops/_custom_dtype via
getattr(binary, op_name). floordiv is the one name in that set registered
with lazy=True, so the getattr materialized it, compiling every dtype
signature on every process start.
Seed those three attributes in init instead. That covers every path
that creates a BinaryOp (builtin enumeration, the specials in
_initialize, delayed UDF materialization, anonymous), so the seeding is
not lost, and laziness survives: floordiv still compiles on first use and
UDT lift behavior is unchanged.
Measured here, fresh process, first
binary.plusaccess: ~648 ms before,~152 ms after (medians of 3 and 5 runs). Treat these as indicative, not
benchmark-grade; load average was ~4 on a shared machine throughout, so
the absolute numbers move but the ~4x gap does not.
This does not make imports free. The ~152 ms that remains is the rest of
_initialize plus namespace setup and is untouched here. It also does not
change what floordiv costs once you use it; the compile is deferred, not
removed. The other four lazily-registered UDFs (rfloordiv, absfirst,
abssecond, rpow) were never in _BUILTIN_UDT_BINARY_OPS and so were never
force-built by this loop.
test_initialize_does_not_build_lazy_udfs covers the invariant in a
subprocess, since _initialize has already run by the time any in-process
test executes.
Stack created with GitHub Stacks CLI • Give Feedback 💬