|
13 | 13 |
|
14 | 14 | import src_method._sweep as sweep_module |
15 | 15 | from src_method import Resources, apply, compress, src |
| 16 | +from src_method._group import DeviceGroup |
16 | 17 | from src_method._kernels import SiteKernels |
17 | 18 | from src_method._plan import ( |
18 | 19 | GPU_MARGIN_FRACTION, |
@@ -475,3 +476,35 @@ def test_too_many_devices_raise() -> None: |
475 | 476 |
|
476 | 477 | with pytest.raises(ValueError, match="visible"): |
477 | 478 | src(*stack, chi_out=4, device="gpu", resources=Resources(devices=count + 1)) |
| 479 | + |
| 480 | + |
| 481 | +def test_group_runs_on_a_non_blocking_stream() -> None: |
| 482 | + """Ranks leave the null stream, keep one stream per device, and see prior work.""" |
| 483 | + group = DeviceGroup("gpu", [0]) |
| 484 | + data = cupy.zeros(1 << 24, dtype=cupy.float32) |
| 485 | + data += 1 # queued on the null stream before the run |
| 486 | + |
| 487 | + def fn(_rank: int) -> tuple[int, float]: |
| 488 | + stream = cupy.cuda.get_current_stream() |
| 489 | + assert stream != cupy.cuda.Stream.null |
| 490 | + return stream.ptr, float((data * 2).sum()) |
| 491 | + |
| 492 | + ((first, total),) = group.run(fn) |
| 493 | + ((second, _),) = group.run(fn) |
| 494 | + |
| 495 | + assert total == 2 * data.size |
| 496 | + assert first == second |
| 497 | + assert cupy.cuda.get_current_stream() == cupy.cuda.Stream.null |
| 498 | + |
| 499 | + |
| 500 | +@pytest.mark.usefixtures("two_gpus") |
| 501 | +def test_two_gpus_run_on_their_own_streams() -> None: |
| 502 | + """Each rank of a group runs on a non-blocking stream of its own device.""" |
| 503 | + group = DeviceGroup("gpu", 2) |
| 504 | + |
| 505 | + def fn(rank: int) -> tuple[int, int]: |
| 506 | + stream = cupy.cuda.get_current_stream() |
| 507 | + assert stream != cupy.cuda.Stream.null |
| 508 | + return rank, stream.device_id |
| 509 | + |
| 510 | + assert group.run(fn) == [(0, 0), (1, 1)] |
0 commit comments