Skip to content

Commit 5c0e26c

Browse files
committed
Raise error if max slices smaller than one slice plus padding slices
See the following PR comments for a discussion on what could be done in the future to try and salvage the pipeline run instead of raising an error: #680.
1 parent 3fecca5 commit 5c0e26c

File tree

3 files changed

+59
-5
lines changed

3 files changed

+59
-5
lines changed

httomo/runner/task_runner.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,20 @@ def determine_max_slices(self, section: Section, slicing_dim: int):
459459
max_slices_methods[idx] = min(max_slices, slices_estimated)
460460
non_slice_dims_shape = output_dims
461461

462+
if (
463+
min(max_slices_methods)
464+
< 1 + self.source.padding[0] + self.source.padding[1]
465+
):
466+
padded_method = next(
467+
method.method_name for method in section.methods if method.padding
468+
)
469+
err_str = (
470+
"Unable to process data due to GPU memory limitations.\n"
471+
f"Please remove method '{padded_method}' from the pipeline, or run on a "
472+
"machine with more GPU memory."
473+
)
474+
raise ValueError(err_str)
475+
462476
section.max_slices = min(max_slices_methods)
463477

464478
def _pass_min_block_length_to_intermediate_data_wrapper(self, section: Section):

tests/runner/test_task_runner.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,48 @@ def test_can_determine_max_slices_with_cpu_large(
228228
assert s[0].max_slices == 64
229229

230230

231+
def test_determine_max_slices_raises_error_if_padded_data_cannot_fit(
232+
mocker: MockerFixture, tmp_path: PathLike
233+
):
234+
METHOD_NAME = "method_requiring_padding"
235+
PADDING = (5, 5)
236+
MAX_SLICES_LESS_THAN_CORE_PLUS_PADDING = 9
237+
data = np.ones((10, 10, 10), dtype=np.float32)
238+
aux = AuxiliaryData(angles=np.ones(data.shape[0], dtype=np.float32))
239+
block = DataSetBlock(data, aux)
240+
loader = make_test_loader(mocker, block=block, padding=PADDING)
241+
padded_gpu_method = make_test_method(
242+
mocker,
243+
method_name=METHOD_NAME,
244+
gpu=True,
245+
padding=True,
246+
memory_gpu=GpuMemoryRequirement(multiplier=2.0, method="direct"),
247+
)
248+
mocker.patch.object(
249+
padded_gpu_method,
250+
"calculate_max_slices",
251+
return_value=(MAX_SLICES_LESS_THAN_CORE_PLUS_PADDING, 0),
252+
)
253+
mocker.patch.object(
254+
padded_gpu_method,
255+
"calculate_output_dims",
256+
return_value=(data.shape[1], data.shape[2]),
257+
)
258+
259+
p = Pipeline(loader=loader, methods=[padded_gpu_method])
260+
t = TaskRunner(p, reslice_dir=tmp_path, comm=MPI.COMM_WORLD)
261+
t._prepare()
262+
s = sectionize(p)
263+
264+
err_str = (
265+
"Unable to process data due to GPU memory limitations.\n"
266+
f"Please remove method '{METHOD_NAME}' from the pipeline, or run on a machine with "
267+
"more GPU memory."
268+
)
269+
with pytest.raises(ValueError, match=err_str):
270+
t.determine_max_slices(s[0], 0)
271+
272+
231273
def test_append_side_outputs(mocker: MockerFixture, tmp_path: PathLike):
232274
p = Pipeline(make_test_loader(mocker), methods=[])
233275
t = TaskRunner(p, reslice_dir=tmp_path, comm=MPI.COMM_WORLD)

tests/testing_utils.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def make_test_loader(
5555
block: Optional[DataSetBlock] = None,
5656
pattern: Pattern = Pattern.all,
5757
method_name="testloader",
58+
padding: tuple[int, int] = (0, 0),
5859
) -> LoaderInterface:
5960
interface: LoaderInterface = mocker.create_autospec(
6061
LoaderInterface,
@@ -66,9 +67,6 @@ def make_test_loader(
6667
)
6768
if block is not None:
6869

69-
# NOTE: Even though the `padding` parameter is unused, this is needed in order to
70-
# replicate the signature of the `make_data_source()` method defined on the
71-
# `LoaderInterface` protocol
7270
def mock_make_data_source(padding) -> DataSetSource:
7371
ret = mocker.create_autospec(
7472
DataSetSource,
@@ -79,7 +77,7 @@ def mock_make_data_source(padding) -> DataSetSource:
7977
chunk_index=block.chunk_index,
8078
slicing_dim=1 if interface.pattern == Pattern.sinogram else 0,
8179
aux_data=block.aux_data,
82-
padding=(0, 0),
80+
padding=padding,
8381
)
8482
slicing_dim: Literal[0, 1, 2] = (
8583
1 if interface.pattern == Pattern.sinogram else 0
@@ -103,7 +101,7 @@ def mock_make_data_source(padding) -> DataSetSource:
103101
mocker.patch.object(
104102
interface,
105103
"make_data_source",
106-
side_effect=mock_make_data_source,
104+
return_value=mock_make_data_source(padding),
107105
)
108106
return interface
109107

0 commit comments

Comments
 (0)