BlockLayout2D.build: check capacity before generating regular origins
c397e08 moved the 64-bit capacity check after np.arange(nby*nbx), so an
over-limit regular layout allocated before rejecting. Check arithmetic
block count (regular) or validated selected count (sparse) first; mock
regression asserts no np.arange before the ValueError.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
68ecaf9e81
commit
93d1cddd27
2 files changed
+17
-6
No files matched your search
@@ -140,13 +140,17 @@ class BlockLayout2D:
|
|||||||
nby, nbx = ny // by, nx // bx
|
nby, nbx = ny // by, nx // bx
|
||||||
ly, lx = by + 2 * halo, bx + 2 * halo
|
ly, lx = by + 2 * halo, bx + 2 * halo
|
||||||
cells = ly * lx
|
cells = ly * lx
|
||||||
if origins is None:
|
if origins is not None:
|
||||||
jy, jx = np.divmod(np.arange(nby * nbx), nbx)
|
selected = _selected_blocks(origins, ny, nx, by, bx)
|
||||||
origins = np.stack([jy * by, jx * bx], axis=1)
|
origins = np.array(origins, dtype=np.int64).reshape(-1, 2) # own copy, never caller storage
|
||||||
selected = _selected_blocks(origins, ny, nx, by, bx)
|
# Capacity from arithmetic before any grid-sized allocation; hole =
|
||||||
origins = np.array(origins, dtype=np.int64).reshape(-1, 2) # own copy, never caller storage
|
# nblocks * cells is below this bound too.
|
||||||
if (len(origins) + 1) * NQ * cells > np.iinfo(np.int64).max // 2:
|
if (nby * nbx if origins is None else len(origins)) * NQ * cells > np.iinfo(np.int64).max // 2:
|
||||||
raise ValueError("blocked storage exceeds 64-bit indexing")
|
raise ValueError("blocked storage exceeds 64-bit indexing")
|
||||||
|
if origins is None:
|
||||||
|
selected = np.arange(nby * nbx)
|
||||||
|
jy, jx = np.divmod(selected, nbx)
|
||||||
|
origins = np.stack([jy * by, jx * bx], axis=1)
|
||||||
|
|
||||||
# Grid block -> stored block, or len(origins) for an absent block, so
|
# Grid block -> stored block, or len(origins) for an absent block, so
|
||||||
# absent cells land exactly on hole = nblocks * cells.
|
# absent cells land exactly on hole = nblocks * cells.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""CPU block layout contracts: ownership, halo pairs, packing and tagged pull."""
|
"""CPU block layout contracts: ownership, halo pairs, packing and tagged pull."""
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -280,6 +281,12 @@ class BlockLayoutTests(unittest.TestCase):
|
|||||||
lx = layout.local_shape[1]
|
lx = layout.local_shape[1]
|
||||||
self.assertEqual(int(mapped.write[0]), int(layout.slot(3 * lx + 0, 1)))
|
self.assertEqual(int(mapped.write[0]), int(layout.slot(3 * lx + 0, 1)))
|
||||||
|
|
||||||
|
def test_capacity_rejected_before_allocation(self):
|
||||||
|
fail = AssertionError("grid-sized allocation before the capacity check")
|
||||||
|
with mock.patch.object(np, "arange", side_effect=fail):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
BlockLayout2D.build(2 ** 30, 2 ** 30, 1, 1)
|
||||||
|
|
||||||
def test_tables_are_readonly(self):
|
def test_tables_are_readonly(self):
|
||||||
layout = BlockLayout2D.build(6, 8, 3, 4)
|
layout = BlockLayout2D.build(6, 8, 3, 4)
|
||||||
for array in (layout.origins, layout.owner, layout.halo_dst, layout.halo_src, layout.halo_kind):
|
for array in (layout.origins, layout.owner, layout.halo_dst, layout.halo_src, layout.halo_kind):
|
||||||
|
|||||||
Reference in new issue
Block a user