[MUH] Fix 7 structural discrepancies vs CCCL — read source, not grep
Fixes found by reading all 17 muh files + 6 CCCL counterpart policy_selectors as full source code input: 1. topk: BLOCK_LOAD_DIRECT → BLOCK_LOAD_VECTORIZE (CCCL SM90+ uses VECTORIZE). bits_per_pass was wrong (muh: ks<=4→9, CCCL: ks>=2→11). items now computed dynamically (4*4/key_size) not hardcoded. 2. reduce: added determinism dispatch — three modes matching CCCL: gpu_to_gpu (BLOCK_REDUCE_RAKING, vec_size=1, LOAD_DEFAULT), run_to_run (WARP_REDUCTIONS, LOAD_LDG, default), not_guaranteed (WARP_REDUCTIONS_NONDETERMINISTIC). Added bi100_det_float32 and bi100_det_float64 tuning structs with SM90 benchmark reference values. 3. batch_memcpy: flat single-tier → SmallBuffer+LargeBuffer two-tier matching CCCL structure (128 threads small, 256 threads large, warp_threshold=128, block_threshold=8192). 4. transform: single BulkPolicy → three-policy structure (VectorizedPolicy + AsyncCopyPolicy + PrefetchPolicy) matching CCCL. items_per_thread computed from bytes_in_flight / (threads * elem_size). 5. compile_test: 17 checks → 33 checks. Now verifies exact values: reduce determinism modes, topk VECTORIZE + bits=11, batch_memcpy two-tier thresholds, transform three-policy structure. 6. gen_patch: added fallback extraction for inline policy_selector values (topk now generates SAMPLING_BLOCK_SIZE patch). 7. MUH_PROJECT_CHECKPOINT.md: 'PRD设计阶段还没有代码' → actual status. 7 files changed, 413 insertions, 265 deletions.
This commit is contained in:
@@ -1,10 +1,5 @@
|
||||
// muh/test/compile_test.cpp — Compile-time verification of muh tuning headers
|
||||
//
|
||||
// This test does NOT require a GPU. It verifies:
|
||||
// 1. All headers parse without errors
|
||||
// 2. All policy_selector functors instantiate and return valid policies
|
||||
// 3. All bi100_* struct values are non-zero (not forgotten placeholders)
|
||||
//
|
||||
// Build: g++ -std=c++17 -I muh/include muh/test/compile_test.cpp -o muh_test
|
||||
// Run: ./muh_test
|
||||
|
||||
@@ -13,125 +8,140 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
// Helper: verify a value is non-zero (catches forgotten TBD placeholders)
|
||||
#define CHECK_NONZERO(expr, name) \
|
||||
do { \
|
||||
auto _v = (expr); \
|
||||
if (_v == 0) { \
|
||||
std::fprintf(stderr, "FAIL: %s == 0 (placeholder not filled)\n", name); \
|
||||
failures++; \
|
||||
} else { \
|
||||
passes++; \
|
||||
} \
|
||||
} while(0)
|
||||
do { auto _v = (expr); if (_v == 0) { std::fprintf(stderr, "FAIL: %s == 0\n", name); failures++; } else { passes++; } } while(0)
|
||||
|
||||
#define CHECK_EQ(expr, expected, name) \
|
||||
do { auto _v = (expr); if (_v != (expected)) { std::fprintf(stderr, "FAIL: %s == %d, expected %d\n", name, (int)_v, (int)(expected)); failures++; } else { passes++; } } while(0)
|
||||
|
||||
#define CHECK_TRUE(expr, name) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
std::fprintf(stderr, "FAIL: %s\n", name); \
|
||||
failures++; \
|
||||
} else { \
|
||||
passes++; \
|
||||
} \
|
||||
} while(0)
|
||||
do { if (!(expr)) { std::fprintf(stderr, "FAIL: %s\n", name); failures++; } else { passes++; } } while(0)
|
||||
|
||||
int main() {
|
||||
using namespace muh::tuning; // bring enum values into scope
|
||||
int passes = 0;
|
||||
int failures = 0;
|
||||
|
||||
auto hw = muh::target_hw;
|
||||
|
||||
// --- Verify hardware descriptor ---
|
||||
CHECK_TRUE(hw.vendor == muh::hardware_capability::vendor_t::iluvatar,
|
||||
"target_hw.vendor == iluvatar");
|
||||
// --- Hardware descriptor ---
|
||||
CHECK_TRUE(hw.vendor == muh::hardware_capability::vendor_t::iluvatar, "target_hw.vendor");
|
||||
CHECK_NONZERO(hw.warp_size, "target_hw.warp_size");
|
||||
CHECK_NONZERO(hw.max_threads_per_block, "target_hw.max_threads_per_block");
|
||||
|
||||
// --- Test reduce policy_selector ---
|
||||
// --- reduce: default (run_to_run) ---
|
||||
{
|
||||
using namespace muh::tuning::reduce;
|
||||
auto ps = policy_selector{
|
||||
.accum_t = muh::tuning::type_t::float32,
|
||||
.operation_t = muh::tuning::op_kind_t::plus,
|
||||
.offset_size = 4,
|
||||
.accum_size = 4,
|
||||
.offset_size = 4, .accum_size = 4,
|
||||
};
|
||||
auto policy = ps(hw);
|
||||
CHECK_NONZERO(policy.multi_tile.threads_per_block,
|
||||
"reduce.float32.threads_per_block");
|
||||
CHECK_NONZERO(policy.multi_tile.items_per_thread,
|
||||
"reduce.float32.items_per_thread");
|
||||
CHECK_NONZERO(policy.multi_tile.vec_size,
|
||||
"reduce.float32.vec_size");
|
||||
|
||||
// Verify known bi100 value matches
|
||||
CHECK_TRUE(policy.multi_tile.threads_per_block > 0 &&
|
||||
policy.multi_tile.threads_per_block <= 1024,
|
||||
"reduce.threads_per_block in [1, 1024]");
|
||||
auto p = ps(hw);
|
||||
CHECK_EQ(p.multi_tile.threads_per_block, 512, "reduce.f32.threads");
|
||||
CHECK_EQ(p.multi_tile.vec_size, 2, "reduce.f32.vec_size");
|
||||
CHECK_EQ(p.multi_tile.reduce_algorithm, BLOCK_REDUCE_WARP_REDUCTIONS, "reduce.f32.algo");
|
||||
}
|
||||
|
||||
// --- Test topk policy_selector ---
|
||||
// --- reduce: deterministic (gpu_to_gpu) ---
|
||||
{
|
||||
using namespace muh::tuning::reduce;
|
||||
auto ps = policy_selector{
|
||||
.accum_t = muh::tuning::type_t::float32,
|
||||
.operation_t = muh::tuning::op_kind_t::plus,
|
||||
.offset_size = 4, .accum_size = 4,
|
||||
.determinism = determinism_t::gpu_to_gpu,
|
||||
};
|
||||
auto p = ps(hw);
|
||||
CHECK_EQ(p.multi_tile.reduce_algorithm, BLOCK_REDUCE_RAKING, "reduce.det.algo=RAKING");
|
||||
CHECK_EQ(p.multi_tile.vec_size, 1, "reduce.det.vec_size=1");
|
||||
CHECK_EQ(p.multi_tile.load_modifier, LOAD_DEFAULT, "reduce.det.load=DEFAULT");
|
||||
}
|
||||
|
||||
// --- reduce: nondeterministic ---
|
||||
{
|
||||
using namespace muh::tuning::reduce;
|
||||
auto ps = policy_selector{
|
||||
.accum_t = muh::tuning::type_t::float32,
|
||||
.operation_t = muh::tuning::op_kind_t::plus,
|
||||
.offset_size = 4, .accum_size = 4,
|
||||
.determinism = determinism_t::not_guaranteed,
|
||||
};
|
||||
auto p = ps(hw);
|
||||
CHECK_EQ(p.multi_tile.reduce_algorithm, BLOCK_REDUCE_WARP_REDUCTIONS_NONDETERMINISTIC,
|
||||
"reduce.nondet.algo=NONDETERMINISTIC");
|
||||
}
|
||||
|
||||
// --- topk: verify VECTORIZE and correct bits_per_pass ---
|
||||
{
|
||||
using namespace muh::tuning::topk;
|
||||
auto ps = policy_selector{.key_size = 2};
|
||||
auto policy = ps(hw);
|
||||
CHECK_NONZERO(policy.threads_per_block, "topk.2B.threads_per_block");
|
||||
CHECK_NONZERO(policy.items_per_thread, "topk.2B.items_per_thread");
|
||||
CHECK_NONZERO(policy.bits_per_pass, "topk.2B.bits_per_pass");
|
||||
CHECK_TRUE(policy.bits_per_pass >= 4 && policy.bits_per_pass <= 11,
|
||||
"topk.bits_per_pass in [4, 11]");
|
||||
// 2-byte keys (fp16 logits — LLM hot path)
|
||||
auto p2 = policy_selector{.key_size = 2}(hw);
|
||||
CHECK_EQ(p2.load_algorithm, BLOCK_LOAD_VECTORIZE, "topk.2B.load=VECTORIZE");
|
||||
CHECK_EQ(p2.bits_per_pass, 11, "topk.2B.bits=11"); // CCCL: case 2 → 11
|
||||
CHECK_EQ(p2.items_per_thread, 8, "topk.2B.items=8"); // 4*4/2=8
|
||||
CHECK_EQ(p2.threads_per_block, 512, "topk.2B.threads=512");
|
||||
|
||||
// 4-byte keys
|
||||
auto p4 = policy_selector{.key_size = 4}(hw);
|
||||
CHECK_EQ(p4.bits_per_pass, 11, "topk.4B.bits=11");
|
||||
CHECK_EQ(p4.items_per_thread, 4, "topk.4B.items=4"); // 4*4/4=4
|
||||
|
||||
// 1-byte keys
|
||||
auto p1 = policy_selector{.key_size = 1}(hw);
|
||||
CHECK_EQ(p1.bits_per_pass, 8, "topk.1B.bits=8");
|
||||
CHECK_EQ(p1.items_per_thread, 16, "topk.1B.items=16"); // 4*4/1=16
|
||||
}
|
||||
|
||||
// --- Test scan policy_selector ---
|
||||
// --- scan: lookback + lookahead ---
|
||||
{
|
||||
using namespace muh::tuning::scan;
|
||||
auto ps = policy_selector{
|
||||
.input_value_size = 4,
|
||||
.accum_size = 4,
|
||||
.offset_size = 4,
|
||||
.input_value_size = 4, .accum_size = 4, .offset_size = 4,
|
||||
.input_type = muh::tuning::type_t::float32,
|
||||
.accum_type = muh::tuning::type_t::float32,
|
||||
.operation_t = muh::tuning::op_kind_t::plus,
|
||||
.is_primitive_accum = true,
|
||||
};
|
||||
auto policy = ps(hw);
|
||||
CHECK_NONZERO(policy.lookback.threads_per_block,
|
||||
"scan.float32.lookback.threads_per_block");
|
||||
CHECK_NONZERO(policy.lookback.items_per_thread,
|
||||
"scan.float32.lookback.items_per_thread");
|
||||
auto p = ps(hw);
|
||||
CHECK_EQ(p.lookback.threads_per_block, 384, "scan.f32.lookback.threads=384");
|
||||
CHECK_EQ(p.lookback.items_per_thread, 22, "scan.f32.lookback.items=22");
|
||||
CHECK_NONZERO(p.lookahead.reduce_and_scan_warps, "scan.f32.lookahead.warps");
|
||||
}
|
||||
|
||||
// --- Test transform policy_selector ---
|
||||
// --- batch_memcpy: two-tier ---
|
||||
{
|
||||
using namespace muh::tuning::batch_memcpy;
|
||||
auto p = policy_selector{}(hw);
|
||||
CHECK_EQ(p.small_buffer.threads_per_block, 128, "batch_memcpy.small.threads=128");
|
||||
CHECK_EQ(p.small_buffer.buffers_per_thread, 4, "batch_memcpy.small.bufs=4");
|
||||
CHECK_EQ(p.small_buffer.warp_level_threshold, 128, "batch_memcpy.small.warp_thresh=128");
|
||||
CHECK_EQ(p.small_buffer.block_level_threshold, 8192, "batch_memcpy.small.block_thresh=8192");
|
||||
CHECK_EQ(p.large_buffer.threads_per_block, 256, "batch_memcpy.large.threads=256");
|
||||
CHECK_EQ(p.large_buffer.bytes_per_thread, 32, "batch_memcpy.large.bytes=32");
|
||||
}
|
||||
|
||||
// --- transform: three-policy ---
|
||||
{
|
||||
using namespace muh::tuning::transform;
|
||||
auto ps = policy_selector{
|
||||
.min_elem_size = 2,
|
||||
.max_elem_size = 2,
|
||||
.num_inputs = 1,
|
||||
.min_elem_size = 2, .max_elem_size = 2, .num_inputs = 1,
|
||||
.all_contiguous = true, .all_trivially_relocatable = true,
|
||||
.requires_stable_address = false,
|
||||
};
|
||||
auto policy = ps(hw);
|
||||
CHECK_NONZERO(policy.bulk.threads_per_block,
|
||||
"transform.bulk.threads_per_block");
|
||||
auto p = ps(hw);
|
||||
CHECK_NONZERO(p.vectorized.threads_per_block, "transform.vectorized.threads");
|
||||
CHECK_NONZERO(p.vectorized.vec_size, "transform.vectorized.vec_size");
|
||||
CHECK_NONZERO(p.async_copy.threads_per_block, "transform.async_copy.threads");
|
||||
CHECK_EQ(p.prefetch.threads_per_block, 256, "transform.prefetch.threads=256");
|
||||
CHECK_EQ(p.fill.threads_per_block, 256, "transform.fill.threads=256");
|
||||
}
|
||||
|
||||
// --- Test batch_memcpy policy_selector ---
|
||||
{
|
||||
using namespace muh::tuning::batch_memcpy;
|
||||
auto ps = policy_selector{};
|
||||
auto policy = ps(hw);
|
||||
CHECK_NONZERO(policy.threads_per_block,
|
||||
"batch_memcpy.threads_per_block");
|
||||
}
|
||||
|
||||
// --- Test for_each policy_selector ---
|
||||
// --- for_each ---
|
||||
{
|
||||
using namespace muh::tuning::for_each;
|
||||
auto ps = policy_selector{};
|
||||
auto policy = ps(hw);
|
||||
CHECK_NONZERO(policy.threads_per_block,
|
||||
"for_each.threads_per_block");
|
||||
CHECK_NONZERO(policy.items_per_thread,
|
||||
"for_each.items_per_thread");
|
||||
auto p = policy_selector{}(hw);
|
||||
CHECK_EQ(p.threads_per_block, 256, "for.threads=256");
|
||||
CHECK_EQ(p.items_per_thread, 4, "for.items=4");
|
||||
}
|
||||
|
||||
// --- Report ---
|
||||
|
||||
Reference in New Issue
Block a user