Repository navigation
[MIOpen] Reduce convolution heuristic overhead - #7333
amd-ahyttine wants to merge 1 commit into
Conversation
| CandidateSelectionResult | ||
| ModelSelectBestCandidate(const std::string& arch, | ||
| const std::string& solver, | ||
| const conv::ProblemDescription& problem, |
There was a problem hiding this comment.
Can we forward declare this instead of adding the include?
There was a problem hiding this comment.
Fixed. #include <miopen/conv/problem_description.hpp> has been removed from ai_candidate_selection.hpp and replaced with a forward declaration of conv::ProblemDescription. The full include is now in ai_candidate_selection.cpp only.
| // Kernel-config embeddings are cached per individual kernel row. Encoding a | ||
| // subset of previously-seen kernels must return identical embeddings to the | ||
| // original call, confirming per-row reuse is correct and not corrupted. | ||
| TEST_P(GPU_CandidateSelection_FP32, KernelEmbeddingCache_Test) |
There was a problem hiding this comment.
Claude found this one:
KernelEmbeddingCache_Test does not test the cache. The test calls model.EncodeKernelConfigs(...) directly, which is a thin wrapper around EncodeKernelConfigsWithFdeep and never
touches KernelEmbeddingCache. The cache is only invoked from GetOrComputeKernelEmbeddings (anonymous namespace, ai_candidate_selection.cpp:106-168). The test would pass even if the cache
code were deleted. Fix by exercising via ModelSelectBestCandidate with overlapping kernel sets, or by exposing the helper.
There was a problem hiding this comment.
Fixed.
Claude tells me:
The test previously called
model.EncodeKernelConfigs()directly, which bypassesGetOrComputeKernelEmbeddingsentirely. It has been rewritten to exercise the cache viaModelSelectBestCandidate: two calls with identical features and kernel params but differentProblemDescriptionobjects (different B-RAM keys, same embedding cache keys). Because the scorer receives identical Tower 1 output and identical cached Tower 2 embeddings, the rankings must be deterministically the same — a failure would indicate the cache returned a corrupted or wrong embedding.
| }; | ||
|
|
||
| std::string | ||
| HashKernelRow(const std::string& arch, const std::string& solver, const std::vector<float>& row) |
There was a problem hiding this comment.
Is there a chance that we get collisions here? From claude:
HashKernelRow collision risk (ai_candidate_selection.cpp:97-104). The size_t hash IS the key; there is no post-hash equality check on the float rows. With small-integer-valued
encodings from EncodeKernelParams, std::hash + boost-XOR-shift produces structured output that elevates collision rate above the 64-bit theoretical floor. A collision returns the
wrong embedding silently and SelectBestCandidateIndices then ranks against it. Fix: build the key from a delimited std::to_string (or hex-encoded bit pattern) of the row.
There was a problem hiding this comment.
Fixed.
I recall Claude also flagging something similar but deemed the likelihood of this happening to be extremely low. Regardless, there's no reason to not properly fix this.
The hash-based key has been replaced with an exact bit-pattern key: each float in the row is memcpy-ed to a uint32_t and appended as hex to the key string, prefixed by arch|solver|row_size. This is collision-free by construction. The function has been renamed KernelRowKey to reflect that it is no longer a hash.
|
We also need to add a Risk Assessment section to this PR message (although I believe we can drop that once we merge). |
Thanks Jonathan, I responded to all your comments and added the RA section to the PR text. |
|
This pull request has been inactive for 25 days and will be marked as stale. If you would like to keep this PR open, please:
This PR will be automatically closed in 5 days if no further activity occurs. |
|
This pull request has been automatically closed due to inactivity (30 days with no updates). If you'd like to continue working on this, feel free to reopen the PR or create a new one. |
Rebased onto develop (squashed). Adds the candidate-selection result cache (B-RAM) and the per-row kernel-config embedding cache (C1) to cut two-tower heuristic overhead on repeated calls. Conflict resolution vs develop: - ai_candidate_selection.cpp: kept develop's LgbmOnly() bypass and ConfigEncoder-ranked logging; added the RAM result-cache short-circuit and GetOrComputeKernelEmbeddings() embedding cache. - ai_heuristics.cpp: took develop's unified TunaNet->LGBM->WTI predictor; dropped the PR's timing log on the removed ND-first block. - conv_ai_candidate_selection_model.cpp: unioned develop's decode tests with the PR's 3 cache tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a60587b to
9994646
Compare
✅ All Checks Passed — Ready for Review
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🎉 All checks passed! This PR is ready for review. |
|
I think we can probably close this again, as it seems to be superseded by #12947 |
|
This pull request has been inactive for 25 days and will be marked as stale. If you would like to keep this PR open, please:
This PR will be automatically closed in 5 days if no further activity occurs. |
|
Superseded by #12947 |
JIRA ID : SILOTIGER-1145
Motivation
On gfx942/gfx950, the AI-based
CandidateSelectionModelheuristics are invoked during solverselection to rank CK kernel configurations. For workloads that encounter the same convolution shape
more than once within a process (e.g. training loops), or the same solver across different shapes,
the model runs in full every time. This overhead can dominate kernel execution time for small shapes.
This PR adds two lightweight in-process RAM caches to eliminate redundant inference.
Risk Assessment
Low risk. All changes are confined to
ai_candidate_selection.cppand its header, plus thecorresponding test file. No public APIs, ABI, kernel implementations, build system, or database
contents are modified. The two caches are in-process only and are bypassed entirely when
MIOPEN_ENABLE_AI_KERNEL_TUNINGis off. On a cache miss the code path is identical to thepre-PR behaviour, so the only observable difference on a hit is a faster return with the same
result. The result cache stores and returns the ranked
CandidateSelectionResultverbatim;the embedding cache is guarded against collisions by using exact float bit-pattern keys. Both
caches are protected by per-instance mutexes. The risk of regression is limited to workloads
that call
ModelSelectBestCandidatefor the same(arch, solver, problem, use_split_k)more than once within a process, which is also where the benefit is observed.
Technical Details
Two caches are added inside
ModelSelectBestCandidate()inai_candidate_selection.cpp:Result cache — caches the full
CandidateSelectionResult(ranked kernel indices + split_kvalues) keyed on
(arch, solver, problem_description, use_split_k). A hit bypasses all modelinference entirely, mirroring the existing TunaNet RAM cache in
ai_heuristics.cpp.Kernel-config embedding cache — the kernel-config encoder output depends only on the kernel
parameters, not on the input shape. Embeddings are cached at individual kernel-row granularity: on
each call only the rows not already in cache are encoded (in a single batched fdeep call), and the
results are stored per row. This allows partial reuse across different shapes that share any subset
of valid CK kernel instantiations, which is the common case when layers differ in spatial dimensions
but share the same solver.
ModelSelectBestCandidate()gains aconst conv::ProblemDescription¶meter (needed as theresult cache key); both call sites in
ai_conv_nd_kernel_tuning_utils.hppare updated accordingly.Test Plan
./bin/test_conv_ai_candidate_selection_modelThree new test cases are added to
GPU_CandidateSelection_FP32:CandidateSelectionRamCache_Test— callsModelSelectBestCandidatetwice with identicalarguments, asserts identical results, logs timing of both calls.
KernelEmbeddingCache_Test— encodes a set of kernels, then encodes a strict subset of thosesame kernels, and asserts the overlapping rows produce byte-identical embeddings, confirming
per-row cache correctness.
CandidateSelectionRamCacheIsolation_Test— populatesuse_split_k=falseanduse_split_k=truecache entries independently and verifies they do not contaminate each other.
Test Result
All 38 tests in
GPU_CandidateSelection_FP32pass. Cache-hit log lines confirmed at log level 6(
MIOPEN_LOG_LEVEL=6) on the second call in each relevant test.Timings from a 2-layer convolutional network run on MI300 (docker image built from each branch).
First-call overhead includes heuristic inference + kernel compilation. Subsequent calls are
served from MIOpen's invoker cache (no heuristics involved).
The 2-5% speedup on the first forward and backwards calls to
features.3corresponds to the kernel-configembedding cache hit logged as:
The 5 kernel rows shared with a previous solver call are not re-encoded. The improvement scales
with the number of shared kernel rows across solvers and shapes; larger networks with more
repeated solver calls will see proportionally greater benefit.
Submission Checklist