#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "moe_dispatch_configs_sm90.cuh" #include "moe_dispatch_configs_sm100.cuh" #include "moe_fwd_bwd_tuning_configs.cuh" namespace { namespace ffi = tvm::ffi; #ifndef LIGER_CUTE_TEST_MODULE_PATH #define LIGER_CUTE_TEST_MODULE_PATH "libliger_cute_kernels.so" #endif constexpr int kTokens = 256; constexpr int kHiddenDim = 2048; constexpr int kIntermediateDim = 768; constexpr int kNumExperts = 16; constexpr int kTopK = 1; constexpr int kCommTileM = 128; constexpr int kBwdRepeats = 3; DLDataType bf16_dtype() { return DLDataType{kDLBfloat, 16, 1}; } DLDataType i32_dtype() { return DLDataType{kDLInt, 32, 1}; } DLDataType i64_dtype() { return DLDataType{kDLInt, 64, 1}; } DLDevice cuda_device(int device_id) { return DLDevice{kDLCUDA, device_id}; } DLDevice cpu_device() { return DLDevice{kDLCPU, 0}; } struct FwdConfig { int compute; int tn1, tk1, s1, ec1, tn2, tk2, s2, ec2, zb, cs, tm, gtm; }; struct BwdConfig { int compute; int ns2, tn1, tk1, s1, tm3, tn3, tk3, s3, en1, en25, en34, cs, tm, gtm; }; #define FWD_CONFIG_C(C, TN1, TK1, S1, EC1, TN2, TK2, S2, EC2, ZB, CS, TM) \ FwdConfig{C, TN1, TK1, S1, EC1, TN2, TK2, S2, EC2, ZB, CS, TM, TM}, #define FWD_CONFIG_G_C(C, TN1, TK1, S1, EC1, TN2, TK2, S2, EC2, ZB, CS, TM, GTM) \ FwdConfig{C, TN1, TK1, S1, EC1, TN2, TK2, S2, EC2, ZB, CS, TM, GTM}, #define FWD_CONFIG_SM90(...) FWD_CONFIG_C(90, __VA_ARGS__) #define FWD_CONFIG_G_SM90(...) FWD_CONFIG_G_C(90, __VA_ARGS__) #define FWD_CONFIG_SM100(...) FWD_CONFIG_C(100, __VA_ARGS__) #define FWD_CONFIG_G_SM100(...) FWD_CONFIG_G_C(100, __VA_ARGS__) std::vector all_fwd_configs() { return { #if LIGER_CUTE_DISPATCH_COMPUTE == 0 || LIGER_CUTE_DISPATCH_COMPUTE == 90 LIGER_MOE_FWD_DISPATCH_CONFIGS_SM90(FWD_CONFIG_SM90, FWD_CONFIG_G_SM90) #endif #if LIGER_CUTE_DISPATCH_COMPUTE == 0 || LIGER_CUTE_DISPATCH_COMPUTE == 100 LIGER_MOE_FWD_DISPATCH_CONFIGS_SM100(FWD_CONFIG_SM100, FWD_CONFIG_G_SM100) #endif }; } #undef FWD_CONFIG_SM100 #undef FWD_CONFIG_G_SM100 #undef FWD_CONFIG_SM90 #undef FWD_CONFIG_G_SM90 #undef FWD_CONFIG_C #undef FWD_CONFIG_G_C #define BWD_CONFIG_C(C, NS2, TN1, TK1, S1, TM3, TN3, TK3, S3, EN1, EN25, EN34, CS, TM) \ BwdConfig{C, NS2, TN1, TK1, S1, TM3, TN3, TK3, S3, EN1, EN25, EN34, CS, TM, TM}, #define BWD_CONFIG_G_C(C, NS2, TN1, TK1, S1, TM3, TN3, TK3, S3, EN1, EN25, EN34, CS, TM, GTM) \ BwdConfig{C, NS2, TN1, TK1, S1, TM3, TN3, TK3, S3, EN1, EN25, EN34, CS, TM, GTM}, #define BWD_CONFIG_SM90(...) BWD_CONFIG_C(90, __VA_ARGS__) #define BWD_CONFIG_G_SM90(...) BWD_CONFIG_G_C(90, __VA_ARGS__) #define BWD_CONFIG_SM100(...) BWD_CONFIG_C(100, __VA_ARGS__) #define BWD_CONFIG_G_SM100(...) BWD_CONFIG_G_C(100, __VA_ARGS__) std::vector all_bwd_configs() { return { #if LIGER_CUTE_DISPATCH_COMPUTE == 0 || LIGER_CUTE_DISPATCH_COMPUTE == 90 LIGER_MOE_BWD_DISPATCH_CONFIGS_SM90(BWD_CONFIG_SM90, BWD_CONFIG_G_SM90) #endif #if LIGER_CUTE_DISPATCH_COMPUTE == 0 || LIGER_CUTE_DISPATCH_COMPUTE == 100 LIGER_MOE_BWD_DISPATCH_CONFIGS_SM100(BWD_CONFIG_SM100, BWD_CONFIG_G_SM100) #endif }; } #undef BWD_CONFIG_SM100 #undef BWD_CONFIG_G_SM100 #undef BWD_CONFIG_SM90 #undef BWD_CONFIG_G_SM90 #undef BWD_CONFIG_C #undef BWD_CONFIG_G_C std::string force_string(const FwdConfig& c) { std::ostringstream os; os << c.tn1 << ',' << c.tk1 << ',' << c.s1 << ',' << c.ec1 << ',' << c.tn2 << ',' << c.tk2 << ',' << c.s2 << ',' << c.ec2 << ',' << c.zb << ',' << c.cs << ',' << c.tm << ',' << c.gtm; return os.str(); } std::string force_string(const BwdConfig& c) { std::ostringstream os; os << c.ns2 << ',' << c.tn1 << ',' << c.tk1 << ',' << c.s1 << ',' << c.tm3 << ',' << c.tn3 << ',' << c.tk3 << ',' << c.s3 << ',' << c.en1 << ',' << c.en25 << ',' << c.en34 << ',' << c.cs << ',' << c.tm << ',' << c.gtm; return os.str(); } bool matches(const BwdConfig& dispatch, const liger::TunedConfigFwdBwd& tuned) { return dispatch.ns2 == tuned.Bwd_NSplit2 && dispatch.tn1 == tuned.Bwd_TileN1 && dispatch.tk1 == tuned.Bwd_TileK1 && dispatch.s1 == tuned.Bwd_Stages1 && dispatch.tm3 == tuned.Bwd_TileM3 && dispatch.tn3 == tuned.Bwd_TileN3 && dispatch.tk3 == tuned.Bwd_TileK3 && dispatch.s3 == tuned.Bwd_Stages3 && dispatch.en1 == tuned.Bwd_EpiChunkN1 && dispatch.en25 == tuned.Bwd_EpiChunkN25 && dispatch.en34 == tuned.Bwd_EpiChunkN34 && dispatch.cs == tuned.Bwd_CommNumStages && dispatch.tm == tuned.Bwd_TileM && dispatch.gtm == tuned.Bwd_TileM; } void expect_tuned_rows_dispatchable( int compute, const liger::TunedConfigFwdBwdTable* tables, int table_count) { const auto configs = all_bwd_configs(); for (int table_index = 0; table_index < table_count; ++table_index) { const auto& table = tables[table_index]; if (table.Compute != compute) continue; for (int i = 0; i < table.count; ++i) { const auto& tuned = table.configs[i]; EXPECT_TRUE(std::any_of( configs.begin(), configs.end(), [&](const BwdConfig& dispatch) { return dispatch.compute == compute && matches(dispatch, tuned); })) << "missing dispatch row for tuned BWD config at index " << i; } } } bool table_contains( const BwdConfig& dispatch, int compute, const liger::TunedConfigFwdBwdTable* tables, int table_count) { for (int table_index = 0; table_index < table_count; ++table_index) { const auto& table = tables[table_index]; if (table.Compute != compute) continue; for (int i = 0; i < table.count; ++i) if (matches(dispatch, table.configs[i])) return true; } return false; } void expect_dispatch_rows_tuned(int compute) { for (const auto& dispatch : all_bwd_configs()) { if (dispatch.compute != compute) continue; EXPECT_TRUE( table_contains( dispatch, compute, liger::kTunedConfigTablesSingle, liger::kNumTunedConfigTablesSingle) || table_contains( dispatch, compute, liger::kTunedConfigTablesMulti, liger::kNumTunedConfigTablesMulti)) << "untuned backward dispatch row: " << force_string(dispatch); } } int compute_dispatch_key() { int dev = 0; cudaDeviceProp prop{}; if (cudaGetDevice(&dev) != cudaSuccess || cudaGetDeviceProperties(&prop, dev) != cudaSuccess) return 0; if (prop.major == 9) return 90; if (prop.major == 10) return 100; return 0; } int initial_device() { int count = 0; if (cudaGetDeviceCount(&count) != cudaSuccess || count <= 0) return 0; const char* rank_envs[] = { "LOCAL_RANK", "SLURM_LOCALID", "OMPI_COMM_WORLD_LOCAL_RANK", "PMI_RANK", }; for (const char* name : rank_envs) { if (const char* value = std::getenv(name)) { return std::atoi(value) % count; } } return 0; } void check_cuda(cudaError_t status, const char* what) { ASSERT_EQ(status, cudaSuccess) << what << " failed: " << cudaGetErrorString(status); } ffi::Module& ffi_module() { static ffi::Module module = ffi::Module::LoadFromFile(LIGER_CUTE_TEST_MODULE_PATH); return module; } ffi::Function module_func(const char* name) { auto opt = ffi_module()->GetFunction(ffi::String(name)); EXPECT_TRUE(opt.has_value()) << "missing TVM FFI function: " << name; return opt.value(); } struct TensorArg { DLTensor dl{}; std::vector shape; std::vector strides; TensorArg() = default; TensorArg(void* data, std::vector dims, DLDataType dtype, DLDevice device) : shape(std::move(dims)) { strides.resize(shape.size()); int64_t stride = 1; for (size_t i = shape.size(); i-- > 0;) { strides[i] = stride; stride *= shape[i]; } dl.data = data; dl.device = device; dl.ndim = static_cast(shape.size()); dl.dtype = dtype; dl.shape = shape.data(); dl.strides = strides.data(); dl.byte_offset = 0; } ffi::TensorView view() const { return ffi::TensorView(&dl); } }; template struct HostBuffer { std::vector data; explicit HostBuffer(size_t n) : data(n) {} TensorArg tensor(DLDataType dtype) { return TensorArg(data.data(), {static_cast(data.size())}, dtype, cpu_device()); } }; struct DeviceBuffer { void* ptr = nullptr; size_t bytes = 0; DeviceBuffer() = default; explicit DeviceBuffer(size_t nbytes) : bytes(nbytes) { check_cuda(cudaMalloc(&ptr, bytes), "cudaMalloc"); check_cuda(cudaMemset(ptr, 0, bytes), "cudaMemset"); } ~DeviceBuffer() { if (ptr) cudaFree(ptr); } DeviceBuffer(const DeviceBuffer&) = delete; DeviceBuffer& operator=(const DeviceBuffer&) = delete; }; std::vector copy_bf16(const DeviceBuffer& buffer) { std::vector host(buffer.bytes / sizeof(uint16_t)); check_cuda(cudaMemcpy( host.data(), buffer.ptr, buffer.bytes, cudaMemcpyDeviceToHost), "cudaMemcpy finite check"); return host; } bool all_finite_bf16(const std::vector& values) { return std::all_of(values.begin(), values.end(), [](uint16_t value) { return (value & UINT16_C(0x7f80)) != UINT16_C(0x7f80); }); } float bf16_to_float(uint16_t value) { uint32_t bits = static_cast(value) << 16; float result; std::memcpy(&result, &bits, sizeof(result)); return result; } bool mean_relative_close( const std::vector& actual, const std::vector& expected) { if (actual.size() != expected.size() || actual.empty()) return false; double relative_error = 0.0; for (size_t i = 0; i < actual.size(); ++i) { const float actual_value = bf16_to_float(actual[i]); const float expected_value = bf16_to_float(expected[i]); relative_error += std::abs(actual_value - expected_value) / std::max(std::abs(expected_value), 1.0e-3f); } return relative_error / actual.size() < 0.15; } struct MoeBuffers { int n_pes; int num_experts; int experts_per_pe; int max_total_slots; int tile_id_slots; int device; DeviceBuffer X; DeviceBuffer dY; DeviceBuffer expert_indices; DeviceBuffer expert_weights; DeviceBuffer all_B; DeviceBuffer all_C; DeviceBuffer all_A; DeviceBuffer Y; DeviceBuffer token_expert_slots; DeviceBuffer tile_expert_ids; DeviceBuffer dX; DeviceBuffer dB; DeviceBuffer dC; DeviceBuffer dA; DeviceBuffer dW; HostBuffer symm_meta; MoeBuffers(int pes, int device_id) : n_pes(pes), num_experts(kNumExperts), experts_per_pe(num_experts / n_pes), max_total_slots(kTokens * kTopK + num_experts * kCommTileM), tile_id_slots(max_total_slots / kCommTileM), device(device_id), X(sizeof(uint16_t) * kTokens * kHiddenDim), dY(sizeof(uint16_t) * kTokens * kHiddenDim), expert_indices(sizeof(int32_t) * kTokens * kTopK), expert_weights(sizeof(uint16_t) * kTokens * kTopK), all_B(sizeof(uint16_t) * experts_per_pe * kIntermediateDim * kHiddenDim), all_C(sizeof(uint16_t) * experts_per_pe * kIntermediateDim * kHiddenDim), all_A(sizeof(uint16_t) * experts_per_pe * kHiddenDim * kIntermediateDim), Y(sizeof(uint16_t) * kTokens * kHiddenDim), token_expert_slots(sizeof(int32_t) * max_total_slots), tile_expert_ids(sizeof(int32_t) * ((max_total_slots + kCommTileM - 1) / kCommTileM)), dX(sizeof(uint16_t) * kTokens * kHiddenDim), dB(sizeof(uint16_t) * experts_per_pe * kIntermediateDim * kHiddenDim), dC(sizeof(uint16_t) * experts_per_pe * kIntermediateDim * kHiddenDim), dA(sizeof(uint16_t) * experts_per_pe * kHiddenDim * kIntermediateDim), dW(sizeof(uint16_t) * kTokens * kTopK), symm_meta(17) { std::vector h_indices(kTokens * kTopK); for (int t = 0; t < kTokens; ++t) h_indices[t] = t % num_experts; check_cuda(cudaMemcpy(expert_indices.ptr, h_indices.data(), h_indices.size() * sizeof(int32_t), cudaMemcpyHostToDevice), "cudaMemcpy expert_indices"); check_cuda(cudaMemset(X.ptr, 0x3f, X.bytes), "initialize X"); check_cuda(cudaMemset(dY.ptr, 0x3f, dY.bytes), "initialize dY"); check_cuda(cudaMemset(expert_weights.ptr, 0x3f, expert_weights.bytes), "initialize expert_weights"); check_cuda(cudaMemset(all_B.ptr, 0x3f, all_B.bytes), "initialize B"); check_cuda(cudaMemset(all_C.ptr, 0x3f, all_C.bytes), "initialize C"); check_cuda(cudaMemset(all_A.ptr, 0x3f, all_A.bytes), "initialize A"); } DLDevice cuda_dev() const { return cuda_device(device); } TensorArg X_tensor() { return TensorArg(X.ptr, {kTokens, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg dY_tensor() { return TensorArg(dY.ptr, {kTokens, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg expert_indices_tensor() { return TensorArg(expert_indices.ptr, {kTokens, kTopK}, i32_dtype(), cuda_dev()); } TensorArg expert_weights_tensor() { return TensorArg(expert_weights.ptr, {kTokens, kTopK}, bf16_dtype(), cuda_dev()); } TensorArg B_tensor() { return TensorArg(all_B.ptr, {experts_per_pe, kIntermediateDim, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg C_tensor() { return TensorArg(all_C.ptr, {experts_per_pe, kIntermediateDim, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg A_tensor() { return TensorArg(all_A.ptr, {experts_per_pe, kHiddenDim, kIntermediateDim}, bf16_dtype(), cuda_dev()); } TensorArg Y_tensor() { return TensorArg(Y.ptr, {kTokens, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg token_expert_slots_tensor() { return TensorArg(token_expert_slots.ptr, {max_total_slots}, i32_dtype(), cuda_dev()); } TensorArg tile_expert_ids_tensor() { return TensorArg(tile_expert_ids.ptr, {tile_id_slots}, i32_dtype(), cuda_dev()); } TensorArg dX_tensor() { return TensorArg(dX.ptr, {kTokens, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg dB_tensor() { return TensorArg(dB.ptr, {experts_per_pe, kIntermediateDim, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg dC_tensor() { return TensorArg(dC.ptr, {experts_per_pe, kIntermediateDim, kHiddenDim}, bf16_dtype(), cuda_dev()); } TensorArg dA_tensor() { return TensorArg(dA.ptr, {experts_per_pe, kHiddenDim, kIntermediateDim}, bf16_dtype(), cuda_dev()); } TensorArg dW_tensor() { return TensorArg(dW.ptr, {kTokens, kTopK}, bf16_dtype(), cuda_dev()); } TensorArg symm_meta_tensor() { return symm_meta.tensor(i64_dtype()); } }; void run_fwd(MoeBuffers& b, int64_t team, const ffi::Function& fwd_fn) { auto X = b.X_tensor(); auto ei = b.expert_indices_tensor(); auto ew = b.expert_weights_tensor(); auto B = b.B_tensor(); auto C = b.C_tensor(); auto A = b.A_tensor(); auto Y = b.Y_tensor(); auto slots = b.token_expert_slots_tensor(); auto tile_ids = b.tile_expert_ids_tensor(); auto meta = b.symm_meta_tensor(); fwd_fn(X.view(), ei.view(), ew.view(), B.view(), C.view(), A.view(), static_cast(b.num_experts), static_cast(kTopK), team, Y.view(), slots.view(), tile_ids.view(), meta.view()); } void run_bwd(MoeBuffers& b, int64_t team, const ffi::Function& bwd_fn) { auto dY = b.dY_tensor(); auto meta = b.symm_meta_tensor(); auto slots = b.token_expert_slots_tensor(); auto tile_ids = b.tile_expert_ids_tensor(); auto ei = b.expert_indices_tensor(); auto ew = b.expert_weights_tensor(); auto B = b.B_tensor(); auto C = b.C_tensor(); auto A = b.A_tensor(); auto dX = b.dX_tensor(); auto dB = b.dB_tensor(); auto dC = b.dC_tensor(); auto dA = b.dA_tensor(); auto dW = b.dW_tensor(); bwd_fn(dY.view(), meta.view(), slots.view(), tile_ids.view(), ei.view(), ew.view(), B.view(), C.view(), A.view(), static_cast(b.num_experts), static_cast(kTopK), team, dX.view(), dB.view(), dC.view(), dA.view(), dW.view()); } struct NvshmemSession { bool initialized = false; ~NvshmemSession() { if (initialized) { module_func("finalize")(); } } }; void run_dispatch_coverage_for_compute(int target_compute) { int dev_count = 0; if (cudaGetDeviceCount(&dev_count) != cudaSuccess || dev_count <= 0) GTEST_SKIP() << "requires CUDA"; const int device = initial_device(); check_cuda(cudaSetDevice(device), "cudaSetDevice"); const int compute = compute_dispatch_key(); if (compute != 90 && compute != 100) GTEST_SKIP() << "requires Hopper-family or Blackwell-family GPU"; if (compute != target_compute) GTEST_SKIP() << "dispatch compute " << target_compute << " is not supported by this GPU family (detected " << compute << ")"; auto uniqueid_nbytes = module_func("uniqueid_nbytes"); auto get_uniqueid = module_func("get_uniqueid"); auto init_with_uniqueid = module_func("init_with_uniqueid"); auto n_pes_fn = module_func("n_pes"); auto team_world_fn = module_func("team_world"); auto configure_fn = module_func("moe_configure_symmetric"); auto pop_fwd_fn = module_func("moe_pop_fwd"); auto pool_clear_fn = module_func("pool_clear_all"); auto fwd_fn = module_func("moe_fused_fwd_bf16"); auto bwd_fn = module_func("moe_fused_bwd_bf16"); HostBuffer uid_nbytes_buf(1); auto uid_nbytes_tensor = uid_nbytes_buf.tensor(i64_dtype()); uniqueid_nbytes(uid_nbytes_tensor.view()); std::vector uid(static_cast(uid_nbytes_buf.data[0])); get_uniqueid(static_cast(reinterpret_cast(uid.data()))); NvshmemSession nvshmem; init_with_uniqueid(static_cast(0), static_cast(1), static_cast(reinterpret_cast(uid.data()))); nvshmem.initialized = true; HostBuffer n_pes_buf(1); auto n_pes_tensor = n_pes_buf.tensor(i32_dtype()); n_pes_fn(n_pes_tensor.view()); const int n_pes = n_pes_buf.data[0]; HostBuffer team_buf(1); auto team_tensor = team_buf.tensor(i64_dtype()); team_world_fn(team_tensor.view()); const int64_t team = team_buf.data[0]; MoeBuffers buffers(n_pes, device); configure_fn(static_cast(kTokens), static_cast(kHiddenDim), static_cast(buffers.num_experts), static_cast(kTopK), static_cast(n_pes), static_cast(1), static_cast(n_pes)); std::vector fwd_configs; for (const auto& c : all_fwd_configs()) if (c.compute == target_compute) fwd_configs.push_back(c); std::vector bwd_configs; for (const auto& c : all_bwd_configs()) if (c.compute == target_compute) bwd_configs.push_back(c); ASSERT_FALSE(fwd_configs.empty()); ASSERT_FALSE(bwd_configs.empty()); std::vector reference_y; unsetenv("LIGER_MOE_BWD_FORCE_CONFIG"); for (const auto& cfg : fwd_configs) { const std::string force = force_string(cfg); setenv("LIGER_MOE_FORCE_CONFIG", force.c_str(), 1); SCOPED_TRACE("fwd " + force); run_fwd(buffers, team, fwd_fn); check_cuda(cudaDeviceSynchronize(), "fwd sync"); auto y = copy_bf16(buffers.Y); EXPECT_TRUE(all_finite_bf16(y)) << "non-finite forward output"; if (reference_y.empty()) reference_y = y; else EXPECT_TRUE(mean_relative_close(y, reference_y)) << "forward output differs from the reference template"; pop_fwd_fn(); } const std::string fwd_force = force_string(fwd_configs.front()); setenv("LIGER_MOE_FORCE_CONFIG", fwd_force.c_str(), 1); std::array, 5> reference_grads; bool have_reference_grads = false; const char* grad_names[] = {"dX", "dB", "dC", "dA", "dW"}; for (const auto& cfg : bwd_configs) { const std::string force = force_string(cfg); setenv("LIGER_MOE_BWD_FORCE_CONFIG", force.c_str(), 1); SCOPED_TRACE("bwd " + force); for (int repeat = 0; repeat < kBwdRepeats; ++repeat) { SCOPED_TRACE("repeat " + std::to_string(repeat)); run_fwd(buffers, team, fwd_fn); run_bwd(buffers, team, bwd_fn); check_cuda(cudaDeviceSynchronize(), "bwd sync"); std::array, 5> grads = { copy_bf16(buffers.dX), copy_bf16(buffers.dB), copy_bf16(buffers.dC), copy_bf16(buffers.dA), copy_bf16(buffers.dW), }; for (size_t i = 0; i < grads.size(); ++i) { EXPECT_TRUE(all_finite_bf16(grads[i])) << "non-finite " << grad_names[i]; if (have_reference_grads) { EXPECT_TRUE(mean_relative_close(grads[i], reference_grads[i])) << grad_names[i] << " differs from the reference backward template"; } } if (!have_reference_grads) { reference_grads = std::move(grads); have_reference_grads = true; } pop_fwd_fn(); } } unsetenv("LIGER_MOE_FORCE_CONFIG"); unsetenv("LIGER_MOE_BWD_FORCE_CONFIG"); pool_clear_fn(); module_func("finalize")(); nvshmem.initialized = false; } } // namespace TEST(MoeApiDispatchSm90, RunsEveryForwardAndBackwardDispatchEntry) { run_dispatch_coverage_for_compute(90); } TEST(MoeApiDispatchSm100, RunsEveryForwardAndBackwardDispatchEntry) { run_dispatch_coverage_for_compute(100); } #if LIGER_CUTE_DISPATCH_COMPUTE == 0 || LIGER_CUTE_DISPATCH_COMPUTE == 90 TEST(MoeApiDispatchSm90, EveryTunedBackwardRowIsDispatchable) { expect_tuned_rows_dispatchable( 90, liger::kTunedConfigTablesSingle, liger::kNumTunedConfigTablesSingle); expect_tuned_rows_dispatchable( 90, liger::kTunedConfigTablesMulti, liger::kNumTunedConfigTablesMulti); expect_dispatch_rows_tuned(90); } #endif #if LIGER_CUTE_DISPATCH_COMPUTE == 0 || LIGER_CUTE_DISPATCH_COMPUTE == 100 TEST(MoeApiDispatchSm100, EveryTunedBackwardRowIsDispatchable) { expect_tuned_rows_dispatchable( 100, liger::kTunedConfigTablesSingle, liger::kNumTunedConfigTablesSingle); expect_tuned_rows_dispatchable( 100, liger::kTunedConfigTablesMulti, liger::kNumTunedConfigTablesMulti); expect_dispatch_rows_tuned(100); } #endif int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }