Commit 879e1ce
perf: Fix near-miss penalty in _morton_order with hybrid ceiling+argsort strategy (#3718)
* tests: Add non-power-of-2 shard shapes to benchmarks
Add (30,30,30) to large_morton_shards and (10,10,10), (20,20,20),
(30,30,30) to morton_iter_shapes to benchmark the scalar fallback path
for non-power-of-2 shapes, which are not fully covered by the vectorized
hypercube path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* tests: Add near-miss power-of-2 shape (33,33,33) to benchmarks
Documents the performance penalty when a shard shape is just above a
power-of-2 boundary, causing n_z to jump from 32,768 to 262,144.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style: Apply ruff format to benchmark file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* changes: Add changelog entry for PR #3717
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* perf: Fix near-miss penalty in _morton_order with hybrid ceiling+argsort strategy
For shapes just above a power-of-2 (e.g. (33,33,33)), the ceiling-only
approach generates n_z=262,144 Morton codes for only 35,937 valid
coordinates (7.3× overgeneration). The floor+scalar approach is even
worse since the scalar loop iterates n_z-n_floor times (229,376 for
(33,33,33)), not n_total-n_floor.
The fix: when n_z > 4*n_total, use an argsort strategy that enumerates
all n_total valid coordinates via meshgrid, encodes each to a Morton code
using vectorized bit manipulation, then sorts by Morton code. This avoids
the large overgeneration while remaining fully vectorized.
Result for test_morton_order_iter:
(30,30,30): 24ms (ceiling, ratio=1.21)
(32,32,32): 28ms (ceiling, ratio=1.00)
(33,33,33): 32ms (argsort, ratio=7.3 → fixed from ~820ms with scalar)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: Address pre-commit CI failures in _morton_order
- Replace Unicode multiplication sign × with ASCII x in comment (RUF003)
- Add explicit type annotation for np.argsort result to satisfy mypy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: Cast argsort result via np.asarray to resolve mypy no-any-return
np.stack returns Any in mypy's view, so indexing into it also returns
Any. Using np.asarray(..., dtype=np.intp) makes the type explicit and
avoids the no-any-return error at the return site.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: Pre-declare order type to resolve mypy no-any-return in _morton_order
np.asarray and np.stack return Any with numpy 2.1 type stubs, causing
mypy to infer the return type as Any. Pre-declaring order as
npt.NDArray[np.intp] before the if/else makes the intended type explicit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Davis Bennett <davis.v.bennett@gmail.com>1 parent 32c7ab9 commit 879e1ce
1 file changed
+40
-46
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1512 | 1512 | | |
1513 | 1513 | | |
1514 | 1514 | | |
1515 | | - | |
1516 | | - | |
1517 | | - | |
1518 | | - | |
1519 | | - | |
1520 | | - | |
1521 | | - | |
1522 | | - | |
1523 | | - | |
1524 | | - | |
1525 | | - | |
1526 | | - | |
1527 | | - | |
1528 | | - | |
1529 | | - | |
1530 | | - | |
1531 | | - | |
1532 | | - | |
1533 | | - | |
1534 | | - | |
1535 | | - | |
1536 | | - | |
1537 | | - | |
1538 | | - | |
1539 | | - | |
1540 | | - | |
1541 | | - | |
| 1515 | + | |
| 1516 | + | |
| 1517 | + | |
| 1518 | + | |
| 1519 | + | |
| 1520 | + | |
| 1521 | + | |
| 1522 | + | |
| 1523 | + | |
| 1524 | + | |
| 1525 | + | |
| 1526 | + | |
| 1527 | + | |
| 1528 | + | |
| 1529 | + | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + | |
1542 | 1534 | | |
1543 | | - | |
| 1535 | + | |
| 1536 | + | |
| 1537 | + | |
| 1538 | + | |
| 1539 | + | |
| 1540 | + | |
| 1541 | + | |
| 1542 | + | |
| 1543 | + | |
| 1544 | + | |
| 1545 | + | |
| 1546 | + | |
| 1547 | + | |
| 1548 | + | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
1544 | 1556 | | |
1545 | | - | |
1546 | | - | |
1547 | | - | |
1548 | | - | |
1549 | | - | |
1550 | | - | |
1551 | | - | |
1552 | | - | |
1553 | | - | |
1554 | | - | |
1555 | | - | |
1556 | | - | |
1557 | | - | |
1558 | | - | |
1559 | | - | |
1560 | | - | |
1561 | | - | |
1562 | | - | |
1563 | 1557 | | |
1564 | 1558 | | |
1565 | 1559 | | |
| |||
0 commit comments