Repository navigation
feat(ci): cap the gfx1250 test container's memory - #14569
Conversation
Four of these containers share one host, so an OOM in a -j 32 build or a test shard could take the host and the other runners' in-flight jobs down with it. Cap each container instead, so the kill lands in its own cgroup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
❌ PR Check — Action Required
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🚫 Please fix the failed policies before requesting reviews. The following policy checks failed:
The |
tony-davis
left a comment
There was a problem hiding this comment.
Thanks Geo! Getting a cap on these containers is the right move, and I think it's a big improvement over the host-wide OOM we had. I'd keep it, and I have a thought about the number plus a couple of questions.
-
I think of the cap as each job's memory budget, so I'd lean toward a lower number. Four of these share one ~499 GiB machine, and at 440g each container can stay under its cap while the total is well over what the host has. The 10-27 failure looks like that case: one job peaked around 393 GiB (under 440g), and a second job pushed the host over. A cap near host/4 (roughly 110 to 120g, leaving room for the OS and Docker) would make it a real boundary. I'm also planning to make hipblaslt-test read the container's limit and skip or fail a too-big case cleanly inside that budget, tracked in AIHPBLAS-5352. That only works if there is a number to read, so the cap and the harness fix should end up working together. I don't know how much memory the
-j 32build needs in this container, so I'm not sure that low is feasible. Would you be open to adding a last step withif: always()that prints the container's cgroup counters (memory.max,memory.peak,memory.events) and never fails the job? Then the next normal run tells us the real build and test peaks, and whether the cap ever fired. I think it needs cgroup v2 and a recent kernel, so it should just print what it finds. -
I'm not sure whether the cap sees the pinned host memory. The big tests allocate with
hipHostMalloc, which goes through the ROCm/KFD stack, and I haven't confirmed those pages are charged to the container's cgroup. Does anyone on the runtime side know? If they aren't, the cap wouldn't help for exactly the case we care about, and thememory.eventsoutput from point 1 would show it without a special test.
Also, the note about --ipc host leaving /dev/shm outside the cgroup is a good catch. Once the harness work and this cap are both in, I'd expect we can drop the lock in #14545 and keep the cap.
440g was not a boundary: four jobs share a ~499 GiB host, so each could stay under its cap while the host still went OOM, which is how the 10-27 failure happened. Size the cap as a per-job budget instead. Add an always-run, never-failing step that prints the container's cgroup memory counters, split into a build peak and an end-of-job peak, so the next runs show the real peaks and whether the cap fired. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Leaves this PR to the cap alone. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
oh i don't think docker really limits that for the tests. we probably need to fix those tests as it'll definitely be a noisy neighbor i'm happy to land this now though and see if it helps! otherwise, we can open another PR to further limit (perhaps env variables for the tests?) |
tony-davis
left a comment
There was a problem hiding this comment.
Thanks Geo! The 110g number and the comment look right to me, and I'm happy to land this and see how it goes. If you're right that Docker doesn't limit the pinned memory the tests use, then the cap won't catch those cases and the fix has to be in the tests, which I'm tracking in AIHPBLAS-5352. I like your env variable idea. I'll add it there as a host-memory budget the test reads (CI would set it to the job's share), and the test would also count the pinned bytes it allocates itself, so it doesn't depend on what Docker charges. Until then I think the lock in #14545 should stay. One small ask, and no need to change this PR: next time a job is running on g03-2, would you mind running docker stats --no-stream during the big tests and telling us what memory the container shows? If it tracks the pinned usage, Docker is charging it. If it stays low, you're right. If a build ever gets OOM-killed at 110g, that's our signal to revisit the number.
TheRock Submodule Bump ActivityNewest first
|
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
… them (#14608) JIRA ID : AIHPBLAS-5352 ## Motivation `hipblaslt-test`, the hipBLASLt test client, keeps the host buffers that each test frees in a pool, so the next test can reuse them instead of allocating again. These buffers are pinned (allocated with `hipHostMalloc`, so the operating system can neither page them out nor reclaim them), and the pool kept every one until the process exited. On the gfx1250 hardware test host, which has about 499 GiB of memory, the 93 cases with 2^31- and 2^32-element sizes enabled in #13504 left about 123 GiB pinned in one process. With 4 CI runners each splitting the suite into 4 parallel test processes, the out-of-memory killer took the runners down on 10-23 and 10-27. Separately, a failed host allocation returns a null buffer, and the test then writes through it. I don't think this stack guarantees the gfx1250 host never runs short. What it does is make the client hold much less pinned memory, give it back sooner, and skip a case with the amounts instead of getting killed, which is the part of the problem the client can fix. If pressure comes back, the 110 GiB container caps from #14569 keep a kill inside one job, and putting the lock back (#14982 removes it) is a one-file revert. So I'm not planning a long soak test before merging. The pool comes from ROCm/hipBLASLt#1202, which reused allocations to speed up `hipblaslt-bench` across about 500,000 small matmuls. Reuse still matters for large buffers: on the test host below, `hipHostMalloc` takes about 0.18 s per GiB and `hipHostFree` about 0.06 s per GiB. ## Technical Details - A pooled host buffer of 1 GiB or more (`kHostPoolRetainLimit` in `d_vector.hpp`) now stays cached only while consecutive tests reuse it. The pool records the test count when a buffer comes back, and `HostPoolReleaseListener`, a new hook that Google Test calls after every test, frees the large buffers that the test just ended did not use. A run of same-shape cases, such as the four transpose combinations (NN, NT, TN, TT) of one size, still shares one allocation. - When a test asks for a large buffer that no cached buffer fits, the pool first frees the large buffers that earlier tests left, then pins new memory. This keeps the peak near one test's footprint when the shape changes. - A host buffer no longer gets the pool's 20% extra capacity when the extra would take it to 1 GiB or more (`host_pool_capacity()`), so a buffer under the limit stays under it. The existing cutoff for skipping the extra is a quarter of device memory, so on gfx1250 nearly every host buffer got it. - `HipHostBuffer::memcheck()` reports a failed pinned allocation, and `testing_matmul` checks every host buffer after allocating, including the four temporary buffers a swizzled A or B is built in. A failure now skips the test with the requested size ("could not allocate N GiB of pinned host memory"). - A pooled host buffer under 1 GiB is released once 100 consecutive tests have not reused it (`kHostPoolSmallIdleTests`). The pool reuses a buffer only for requests between half its size and its size, so without this a run of shrinking requests kept every earlier buffer pinned. - In `hipblaslt-bench`, a failed host allocation now exits with a failure, as a failed device allocation already did, instead of reporting success. - Device memory behaves as before. `hipblaslt-bench` does not install the listener, so it never frees large buffers early; it does see the 20% change for host buffers of 1 GiB or more. The hipBLASLt library itself is unchanged. This is the first of four stacked PRs for AIHPBLAS-5352. #14609 makes the client's host-memory query respect container memory limits, #14610 runs the memory preflight for every matmul case, and #14982 drops the host-wide lock in the gfx1250 workflow. ## Test Plan - `HostMemory_pre_checkin` (new, `clients/tests/src/host_memory_gtest.cpp`, 6 tests; the `_pre_checkin` suffix puts it in the standard tier the PR checks run). The tests check that a large buffer survives one test boundary and is freed after the next, that a same-shape test reuses it (what the first test wrote is still there), that a large miss frees two buffers earlier tests left, that a 1 GiB request gets exactly 1 GiB, that a small buffer is released after 100 tests that do not reuse it (and kept when one does), and that a failed allocation leaves a null buffer whose size is reported. A 0.9 GiB request gets exactly 0.9 GiB (the old rule gave it 1.08 GiB, over the limit). The tests skip, naming the buffer, on a host that cannot pin that much, and the reuse test skips on a Windows host where the pool frees its idle buffers before every request. A temporary variant without the small-buffer aging failed the new test. - To confirm the tests can fail, I built two temporary variants of the pool. One undid four behaviors at once (no release at the end of a test, no release on a miss, the 20% extra restored, `memcheck()` always succeeding), and it failed 4 of the 5 tests. The other freed large buffers whether or not a test used them, and it failed 3. Every test failed under at least one variant. - The 82 large-memory cases that apply to gfx90a (the five name patterns from #14545), and the `smoke` and `quick` tiers, on develop and on this branch, for wall time and peak resident memory. - Handle leaks: #14714 and #14700 on develop give the matrix layouts, matmul descriptors and HIP events owners, so an early return no longer leaks them. To check that with this stack's new early returns, a preloaded library counted creates and destroys of all three, on the full stack, over 1,892 matmul cases, once normally and once under a 1 MiB host limit so that most cases skip at the preflight. - Adjacent: the `FastCheck_pre_checkin` and `FastCheckDevice_pre_checkin` suites, including `memory_shortfall_reclaims_idle_buffers`, which uses the pool's existing `ReleaseCached()`. `hipblaslt-bench` was built, because it compiles the non-gtest form of the new check, but not run. ## Test Result One MI210 (gfx90a) on a shared host with 2.2 TB of memory. This branch's clients were built against the 10-25 TheRock nightly hipBLASLt gfx90a device library. Each pair of runs went back to back, but the host was busy (load average near 300), so the times carry some noise. | Measurement | develop | this PR | | --- | --- | --- | | 82 large-memory cases, wall time | 316.9 s | 313.8 s | | 82 large-memory cases, peak resident memory | 81.9 GiB | 49.2 GiB | | 82 large-memory cases, mean resident memory | 66.9 GiB | 16.6 GiB | | Resident memory after the last large case | 74 GiB | 2 GiB | | `smoke` + `quick` (9,171 tests), two runs | 66.6 s, 62.0 s | 63.6 s, 64.1 s | - Large-memory cases: 82 of 82 passed on both. `smoke` + `quick`: 9,169 passed and 2 skipped (`GroupedMatmul.smoke_Fp16NN` and `smoke_Bf16NN`, which skip on this GPU) on both. - `HostMemory_pre_checkin`, `FastCheck_pre_checkin` and `FastCheckDevice_pre_checkin`: 64 of 64 passed. - A first version that freed every large buffer at the end of each test took 449.6 s for the large cases against 318.2 s on develop, which is why the pool keeps a buffer while the next test reuses it. - Handle leaks: 0 layouts, descriptors or events left, both normally (13,580 created) and with 409 cases skipped at the preflight (11,235 created). - With the small-buffer aging, `smoke` + `quick` passed 9,225 tests in 59.3 s with peak resident memory 2.18 GiB. - gfx1250: this PR alone passed the hardware workflow's hipBLASLt tests in [40934683157](https://github.com/ROCm/rocm-libraries/actions/runs/40934683157), and Archana dispatched the full stack in [41582794047](https://github.com/ROCm/rocm-libraries/actions/runs/41582794047), which passed hipBLASLt twice with no kills and no memory skips. Her run on the rebased stack, [41717825380](https://github.com/ROCm/rocm-libraries/actions/runs/41717825380), was clean too. All three kept the host-wide lock; #14982 covers the run without it. - Automated GitHub checks are pending. ## Submission Checklist - [x] Look over the contributing guidelines at https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests. ## Risk level Medium (3/5). Every `hipblaslt-test` case passes through the new listener and the host pool, so a mistake here would affect the whole suite. The hipBLASLt library and device behavior are unchanged; only the test and bench clients change. The bench sees the 20% change near 1 GiB, and now exits with a failure when a host allocation fails. ## Related - Work tracking: [AIHPBLAS-5352](https://amd-hub.atlassian.net/browse/AIHPBLAS-5352), fixes 1 (pool retention) and 4 (null host buffer) of its list. - Cause: #13504. CI workaround this work is meant to replace: #14545, merged on 10-28. - Where the pool came from: ROCm/hipBLASLt#1202. Windows already clears the pool on smaller-memory hosts for the same reason: #4793, #6121. ## Device / Architecture Coverage Host-side test-client code only, with no kernel, library or kernel-selection change, so it does not depend on the GPU architecture and the standard PR checks are enough. Measured on gfx90a. ## Flags / Guardrails None. The 1 GiB threshold is the constant `kHostPoolRetainLimit`. [AIHPBLAS-5352]: https://amd-hub.atlassian.net/browse/AIHPBLAS-5352?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…ainer limits (#14609) JIRA ID : AIHPBLAS-5352 ## Motivation Before `hipblaslt-test`, the hipBLASLt test client, pins a host buffer, it checks that enough host memory is free, so that a shortage ends in a skipped test rather than a killed process. It asked two different questions to do that: `h_memory` read `freeram` from `sysinfo()`, and the `fast_check` memory preflight read `MemAvailable` from `/proc/meminfo`. Neither looks at the memory limit of a Linux control group (cgroup), the kernel feature that containers and CI runners use to cap a job's memory, and inside a container both report the whole host. A test inside a container with a memory limit therefore sees plenty of memory, pins past the limit, and the kernel kills the process. On this PR's test host, a large `stress` case inside an 8 GiB limit is killed within 4 seconds on develop, and also with only the previous PR in this stack. ## Technical Details - New `available_host_memory_bytes()` in `clients/common/include/host_memory.hpp` and `src/host_memory.cpp`. On Linux it returns the smaller of `MemAvailable` and the room left under each cgroup memory limit, from the process's own cgroup (read from `/proc/self/cgroup`) up to the root of the hierarchy. It reads cgroup v2 (`memory.max` minus `memory.current`) and v1 (`memory.limit_in_bytes` minus `memory.usage_in_bytes`). Page cache the kernel can drop does not count as used: `active_file` and `inactive_file` in `memory.stat`, less dirty and writeback pages. A removed cgroup, which `/proc/self/cgroup` lists with a ` (deleted)` suffix, still has its limit applied. - The cgroup's path is translated through its mount in `/proc/self/mountinfo` (the cgroup2 mount, or the v1 mount that carries the memory controller, such as `cpu,memory`), and the walk never goes above that mount. Without mountinfo it falls back to `/sys/fs/cgroup`. A container may list its cgroup as `/` or by the host's path, which does not exist inside the container; the walk always checks the top it sees, which is where the container's own limit is. - `/proc/meminfo` and the cgroups each cap the answer on their own, so a full container reports 0 even when `/proc/meminfo` cannot be read. - `h_memory` (in `d_vector.hpp`) and `fast_check_memory_shortfall()` both call it on Linux. For `h_memory` this also changes the base value from `freeram` to `MemAvailable`, which counts reclaimable page cache, as the preflight already did. On this host `freeram` was 181 GiB and `MemAvailable` about 2,070 GiB, because most memory held page cache. When the query has no answer at all, `h_memory` falls back to `freeram`, as before. Inside an 8 GiB cgroup holding 6 GiB of page cache, active or inactive, a 6 GiB `hipHostMalloc` succeeded and the kernel reclaimed the cache, so counting the cache as available matches what pinning can actually get. - The managed-memory (`use_HMM`) branch of `d_memory` uses the same check, as it did with `freeram`. In these tests a managed buffer is first written on the host, so its pages are charged to the cgroup until they migrate, and the host headroom is a conservative bound. No HMM case (they are in the `full` tier) has been run under a limit yet. - Windows keeps its existing behavior at each call site: `h_memory` uses half of physical memory, and the preflight uses available physical memory. - `HIPBLASLT_CLIENT_HOST_MEMORY_LIMIT`, a new optional environment variable, caps the budget for a machine with no container limit (a development box, Windows, or bare metal), and lets a test force a memory skip. It can only lower the budget: a full container stays full, and when the system cannot be read the limit is the budget. The value is a byte count with an optional K, M, G or T suffix in powers of 1024, such as `64G`; an unreadable value is ignored with one warning. CI does not need to set it. Geo Min suggested it on #14569. - `available_host_memory_bytes()` returns `std::optional`: nothing when the system cannot be read, and 0 when no memory is left, so a full container or a limit of 0 makes the preflight skip instead of letting the case run (James Newling's commit). - The limit caps the free memory each query reports. It does not limit what a process allocates in total, and it reserves nothing against other processes; `host_memory.hpp` and `TESTING.md` say so. - The file paths the query reads are parameters (`HostMemorySources`), so tests can point it at temporary files. This is the second of four stacked PRs for AIHPBLAS-5352 and depends on #14608. With #14608, a pinned allocation that does not fit skips the test with the requested size; this PR makes the container's limit part of what "fits" means. ## Test Plan - `HostMemory_pre_checkin.query_*` (13 new tests in `clients/tests/src/host_memory_gtest.cpp`). Twelve build a temporary `/proc/meminfo`, `/proc/self/cgroup`, `/proc/self/mountinfo` and cgroup tree and check the result for: no cgroup; a v2 limit below `MemAvailable`, with page cache subtracted; a v2 cgroup with no limit (`max`); a parent cgroup with a tighter limit than the process's own; a container listing a host path, with its limit at the top, also when the mount's root is another cgroup; a mount of a subtree (`/runner`) with a tighter limit below it; a v1 memory controller mounted with cpu; cgroup v1, limited and unlimited; a full container whose usage is mostly active page cache, with and without dirty pages; a removed cgroup; a full container with no `/proc/meminfo`; and missing, unreadable or over-limit files. The thirteenth only checks that the real files on the test host are readable and that its cgroups do not raise `MemAvailable`. - `HostMemory_pre_checkin.no_host_memory_left_makes_the_preflight_skip` checks a budget of 0. The test helpers restore any `HIPBLASLT_CLIENT_HOST_MEMORY_LIMIT` the run started with, and the fake-host tests clear it while they run (James Newling's commit). - `HostMemory_pre_checkin.limit_*` (4 new tests): the size format, including suffixes, overflow and malformed values; that the variable lowers the budget, never raises it, and leaves a full container at 0; that it is the budget when `/proc/meminfo` cannot be read; and that `fast_check_memory_shortfall()` reports "needs 2 GiB of host memory and 1 GiB is available" under a 1 GiB limit (skipped on a runner with less than 2 GiB free). The pool tests clear the limit while they run, since pinning past it is what they test. - To confirm the tests can fail, I built temporary variants. One stopped the cgroup walk at the process's own cgroup and counted page cache as used, and it failed 3 of the first 8 `query_*` tests. Another ignored cgroups altogether, and it failed 5; the three tests that passed under both check behavior with no limit set. A variant that ignored the environment variable failed 3 of the 4 `limit_*` tests (all but the format test), and one that let the variable replace a full container's 0 failed the cap test. The later fixes each have a test that a variant without the fix failed: no mount translation (2 tests), no v1 mount lookup, reading the cgroups only with `/proc/meminfo`, and walking above the mount. - On the real host: one `stress` case inside a transient 8 GiB cgroup (`systemd-run --user --scope -p MemoryMax=8G -p MemorySwapMax=0`), on develop, on the previous PR alone, and on this PR. - Adjacent: the rest of `HostMemory_pre_checkin`, `FastCheck_pre_checkin` and `FastCheckDevice_pre_checkin` (the preflight's suites), and the `smoke` and `quick` tiers. `hipblaslt-bench` was built but not run. ## Test Result One MI210 (gfx90a), with this branch's clients built against the 10-25 TheRock nightly hipBLASLt gfx90a device library. - `stress_matmul_size_t_index_overflow_f32_..._NN_2048_2048_2048_2_2048_2048_4_1049100_1049100_1` inside an 8 GiB limit: develop and the previous PR alone were killed (exit code 137) within 4 seconds. With this PR the test was skipped with "could not allocate 8 GiB of pinned host memory" and the process exited 0. - `HostMemory_pre_checkin`, `FastCheck_pre_checkin` and `FastCheckDevice_pre_checkin`: all passed with no limit set and with `HIPBLASLT_CLIENT_HOST_MEMORY_LIMIT` at 4G, 1G and 64M for the whole run. Inside a 1.5 GiB cgroup, the preflight-limit test skips instead of failing. - On the same host, 4 GiB pinned with `hipHostMalloc` inside an 8 GiB cgroup raised its `memory.current` from 0.01 GiB to 4.03 GiB, so the container's headroom already counts the clients' pinned buffers. - `smoke` + `quick`: 9,169 passed and 2 skipped (`GroupedMatmul.smoke_Fp16NN` and `smoke_Bf16NN`, which skip on this GPU), the same as develop, in 62.3 s. - Not covered: Windows (unchanged by design), and a real cgroup v1 host. The v1 path is checked only against temporary files. - Automated GitHub checks are pending. ## Submission Checklist - [x] Look over the contributing guidelines at https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests. ## Risk level Low (2/5). Every pinned host allocation in the test and bench clients now consults the new query, so a wrong answer would skip tests that should run, or let one run that should skip. Outside a memory-limited cgroup the answer is `MemAvailable`, which the preflight already used. The hipBLASLt library and device behavior are unchanged. ## Related - Work tracking: [AIHPBLAS-5352](https://amd-hub.atlassian.net/browse/AIHPBLAS-5352), fix 3 (container-aware memory budget, with the override) of its list. - Depends on #14608. Next in the stack: #14610. - #14569 caps each gfx1250 test container at 110 GiB, which is the budget this query reads there. ## Device / Architecture Coverage Host-side test-client code only, with no kernel, library or kernel-selection change, so it does not depend on the GPU architecture and the standard PR checks are enough. ## Flags / Guardrails `HIPBLASLT_CLIENT_HOST_MEMORY_LIMIT` (optional, unset by default) caps the host-memory budget. It is documented in `TESTING.md`. [AIHPBLAS-5352]: https://amd-hub.atlassian.net/browse/AIHPBLAS-5352?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: James Newling <james.newling@gmail.com>
…st memory skips (#14610) JIRA ID : AIHPBLAS-5352 ## Motivation Before allocating, `hipblaslt-test`, the hipBLASLt test client, estimates how much device and host memory a matmul case needs, and skips the case with both amounts when either does not fit. That memory preflight ran only for `fast_check` cases. The `stress` cases checked with `unit_check`, such as `matmul_size_t_index_overflow` and its `_hpa` variant, pin up to about 48 GiB of host memory each on gfx90a and allocated without any check, so on a host or container without room they were killed rather than skipped. Memory skips were also easy to miss: a device out-of-memory error was reported as a passing test, and nothing summarized which cases did not run. ## Technical Details - Every `hipblaslt-test` matmul case in `testing_matmul` now runs the preflight; `hipblaslt-bench` still runs it only for `fast_check` cases. One estimate in `testing_matmul.hpp` serves both kinds of case. Besides the buffers it already counted, it now adds the pool's 20% extra capacity on host buffers below 1 GiB, the CPU reference's float or double copies of one batch of A and B (and of D when the output type is not the compute type), the temporary buffers a swizzled A or B is built in, and one set of buffers per batch in pointer-array mode. - `matmul_batch_offset` cases go through their own function, `testing_matmul_batch_offset`, which now runs the same preflight. Its estimate counts the four host and device buffers, the reference D and the 128 MiB workspace bound. - Every stop for lack of memory goes through one macro, `SKIP_FOR_MEMORY`: the preflight, a failed pinned host allocation, and a device out-of-memory error. Each one skips the test with a message that begins "Not enough memory:", and the end of the run lists those tests under `[ MEMORY ]`, one line each with the amounts. The unused `LIMITED_MEMORY_STRING` messages are removed. - `HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1` turns those skips into failures, so a lane on a large-memory host can require that every case ran. - `HIPBLASLT_TEST_CHECK_MEMORY_ESTIMATE=1` (Linux) checks the estimate on real cases. For a case estimated at 256 MiB or more, it resets the process's peak resident memory (`VmHWM`, which counts pinned and pageable memory alike), and fails the case when the peak rose more than the estimate plus 10% and 256 MiB. It leaves out the first case in a process, which also loads the kernel library (about 0.6 GiB here). - Behavior change for reviewers to weigh: a device out-of-memory error at allocation (`CHECK_DEVICE_ALLOCATION`) used to call `SUCCEED()`, which counts as a pass. It is now a skip. On a GPU with enough memory nothing changes; on a smaller one those cases move from "passed" to "skipped", which is what they were. This is the last of three stacked PRs for AIHPBLAS-5352 and depends on #14609, which depends on #14608. ## Test Plan - `MatmulMemory_pre_checkin` (2 new tests in `clients/tests/src/matmul_gtest.cpp`). One runs a 4096 x 4096 x 16 f32 `unit_check` case under a 64 MiB `HIPBLASLT_CLIENT_HOST_MEMORY_LIMIT` with `HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1`, and expects the preflight's message. The other runs two cases under `HIPBLASLT_TEST_CHECK_MEMORY_ESTIMATE=1` and bounds the estimate from both sides: f32 8192 x 8192 x 16, where six 256 MiB host buffers with their 20% extra make up the estimate, and f16 16384 x 16 x 16384, where the CPU reference's 1 GiB float copy of A is most of it. Together they take about 3 s and pin up to about 1.9 GiB. - `MatmulMemory_pre_checkin.preflight_stops_a_batch_offset_case_that_does_not_fit` does the same for a `matmul_batch_offset` case. The test helpers restore any environment variable the run started with (James Newling's commit). - `HostMemory_pre_checkin.memory_shortages_are_listed_with_their_reasons` checks the `[ MEMORY ]` summary. - To confirm the tests can fail, I built three temporary variants. Dropping the 20% extra from the estimate failed the f32 case (estimate 1.5 GiB, used 1.93 GiB), dropping the reference copies failed the f16 case (0.606 GiB against 1.67 GiB), and running the preflight only for `fast_check` again failed both tests. - The standard tier (`*smoke*:*quick*:*pre_checkin*`) on develop, on this PR, and on this PR with `HIPBLASLT_TEST_CHECK_MEMORY_ESTIMATE=1`. - The 82 large-memory cases that apply to gfx90a (the five name patterns from #14545): normally, with the estimate check, and inside a transient 8 GiB cgroup (`systemd-run --user --scope -p MemoryMax=8G -p MemorySwapMax=0`). ## Test Result One MI210 (gfx90a), with this branch's clients built against the 10-25 TheRock nightly hipBLASLt gfx90a device library. - Standard tier: develop passed 19,695 and skipped 26; this PR passed 19,715 and skipped the same 26. The 20 extra passes are the new tests across the three PRs, so the preflight added no skips there. With the estimate check on, nothing failed. Peak resident memory for the tier fell from 30.1 GiB to 21.5 GiB, mostly from #14608. - In the estimate tests the f32 case was estimated at 1.94 GB and used 1.94 GB, and the f16 case 1.73 GB and 1.71 GB. - Large-memory cases: 82 of 82 passed (327.5 s, peak resident memory 49.2 GiB). With the estimate check all 82 passed again; for the 35 `stress` cases it measured (the 36th was the first case in the process), the rise in peak resident memory was between 0.996 and 1.003 times the estimate, which reached 48.1 GiB. The 46 `fast_check` cases estimate less than 256 MiB of host memory each, so the check does not apply to them. - Inside the 8 GiB cgroup: develop was killed (exit code 137) within 4 seconds, at the first `stress` case. This PR exited 0 after 84 s: the 46 `fast_check` cases passed, and the 36 `stress` cases skipped and were listed, for example "this case needs 16.2 GiB of host memory and 7.41 GiB is available". - `matmul_batch_offset_large`'s shape (2^32-element offsets, run by hand on gfx90a because the YAML case is limited to gfx942, gfx950 and gfx1250): the estimate was 51.55 GB and the measured rise 51.51 GB. - `smoke`, the memory and `fast_check` suites and the `batch_offset` cases: 2,802 passed and 9 `GroupedMatmul` cases skipped, both with nothing set and with `HIPBLASLT_CLIENT_HOST_MEMORY_LIMIT=4G` and `HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1` set for the whole run. No memory skips. - gfx1250: Archana dispatched the full stack in [41582794047](https://github.com/ROCm/rocm-libraries/actions/runs/41582794047). It passed hipBLASLt twice (4 shards and all 6 large groups), with no kills and an empty `[ MEMORY ]` list, with the large-case phase still separate from the shards. Shards still check memory one moment at a time and reserve nothing; whether that matters with the lock removed needs its own runs. - Automated GitHub checks are pending. ## Submission Checklist - [x] Look over the contributing guidelines at https://github.com/ROCm/TheRock/blob/main/GOVERNANCE.md#pull-requests. ## Risk level Medium (3/5). Every `hipblaslt-test` matmul and batch-offset case now runs the preflight, so an estimate that is too high would skip cases that would have run; the standard tier shows the same skips as develop, and the estimate tests bound overestimates. Device out-of-memory errors now report as skips instead of passes. The hipBLASLt library and device behavior are unchanged. ## Related - Work tracking: [AIHPBLAS-5352](https://amd-hub.atlassian.net/browse/AIHPBLAS-5352), fixes 2 (preflight for every case, one estimator) and 5 (visible memory skips) of its list. - Depends on #14609 and #14608. - #14545 runs the large cases after the shards behind a host-wide lock, and #14569 caps each gfx1250 container at 110 GiB. With this stack and the cap in place, a follow-up change to the gfx1250 workflow can remove the lock; the large cases keep their own phase until the question of several shards in one container (fix 6 in the ticket) is settled. ## Device / Architecture Coverage Host-side test-client code only, with no kernel, library or kernel-selection change, so the standard PR checks are enough for correctness. The gfx1250 hardware run in the ticket's acceptance checks the result where the failure happened. ## Flags / Guardrails `HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP` and `HIPBLASLT_TEST_CHECK_MEMORY_ESTIMATE`, both off by default and documented in `TESTING.md`. [AIHPBLAS-5352]: https://amd-hub.atlassian.net/browse/AIHPBLAS-5352?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: James Newling <james.newling@gmail.com>
The gfx1250 workflow ran the large-memory hipBLASLt tests behind a flock shared by every runner on the host, so jobs took turns. With the client now checking each case's memory and freeing large host buffers between tests (AIHPBLAS-5352), and each job's container capped (#14569), drop the lock and its /ci-locks mount. The large tests still run after the shards, one group per process. The large phase now sets HIPBLASLT_TEST_FAIL_ON_MEMORY_SKIP=1, so a large case that skips for memory fails the job, and every test process runs under /usr/bin/time -v. The job summary lists each process's peak resident memory and every test that stopped for memory. JIRA ID : AIHPBLAS-5352 Co-authored-by: Cursor <cursoragent@cursor.com>
Four of these containers share one host, so an OOM in a -j 32 build or a test shard could take the host and the other runners' in-flight jobs down with it. Cap each container instead, so the kill lands in its own cgroup.