/* * Vortex Exact Attention isolated capability probes. * * The MMA operand signatures are adapted from SageAttention 2.2.0 csrc/mma.cuh * (commit d1a57a546c3d395b1ffcbeecc66d81db76f3b4b5), Apache-2.0, which records * prior adaptation from FlashInfer 0.1.5. This file does not implement attention. */ #include #include #include #include #include #include #include #include #include #define CUDA_CHECK(call) do { \ cudaError_t error_ = (call); \ TORCH_CHECK(error_ == cudaSuccess, cudaGetErrorString(error_)); \ } while (0) namespace { constexpr int kThreads = 320; constexpr int kProducerThreads = 192; constexpr int kSlotBytes = 8192; constexpr int kKvSlotBytes = 8192; constexpr int kDynamicSharedBytes = 50 * 1024; __device__ __forceinline__ void ready_arrive(int slot) { if (slot == 0) asm volatile("bar.arrive 0, 320;" ::: "memory"); else asm volatile("bar.arrive 1, 320;" ::: "memory"); } __device__ __forceinline__ void ready_sync(int slot) { if (slot == 0) asm volatile("bar.sync 0, 320;" ::: "memory"); else asm volatile("bar.sync 1, 320;" ::: "memory"); } __device__ __forceinline__ void free_arrive(int slot) { if (slot == 0) asm volatile("bar.arrive 2, 320;" ::: "memory"); else asm volatile("bar.arrive 3, 320;" ::: "memory"); } __device__ __forceinline__ void free_sync(int slot) { if (slot == 0) asm volatile("bar.sync 2, 320;" ::: "memory"); else asm volatile("bar.sync 3, 320;" ::: "memory"); } template __global__ __launch_bounds__(kThreads, 1) void vea_b_handoff_probe(int64_t* result, int epochs, bool payload) { extern __shared__ uint8_t shared[]; uint8_t* scores = shared; uint8_t* k_slot = shared + 2 * kSlotBytes; uint8_t* v_slot = k_slot + 2 * kKvSlotBytes; using BlockBarrier = cuda::barrier; BlockBarrier* barriers = reinterpret_cast(shared + 48 * 1024); const int tid = threadIdx.x; const bool producer = tid < kProducerThreads; int64_t checksum = 0; int completed_epoch = -1; if constexpr (UseMbarrier) { if (tid < 4) init(barriers + tid, kThreads); __syncthreads(); } if (producer) { for (int epoch = 0; epoch < epochs; ++epoch) { const int slot = epoch & 1; if (epoch >= 2) { if constexpr (UseMbarrier) barriers[2 + slot].arrive_and_wait(); else free_sync(slot); } if (payload) { if (tid < 64) { for (int index = tid; index < kKvSlotBytes; index += 64) { k_slot[slot * kKvSlotBytes + index] = static_cast((epoch + index * 3) & 255); v_slot[slot * kKvSlotBytes + index] = static_cast((epoch + index * 5) & 255); } } else { const int row = tid - 64; uint8_t* row_slot = scores + slot * kSlotBytes + row * 64; #pragma unroll for (int column = 0; column < 64; ++column) { row_slot[column] = static_cast((epoch + row * 17 + column) & 255); } } } __threadfence_block(); if constexpr (UseMbarrier) (void)barriers[slot].arrive(); else ready_arrive(slot); completed_epoch = epoch; } if constexpr (UseMbarrier) barriers[2 + ((epochs - 1) & 1)].arrive_and_wait(); else free_sync((epochs - 1) & 1); if (epochs > 1) { if constexpr (UseMbarrier) barriers[2 + ((epochs - 2) & 1)].arrive_and_wait(); else free_sync((epochs - 2) & 1); } } else { const int consumer = tid - kProducerThreads; for (int epoch = 0; epoch < epochs; ++epoch) { const int slot = epoch & 1; if constexpr (UseMbarrier) barriers[slot].arrive_and_wait(); else ready_sync(slot); if (payload) { const uint8_t* row_slot = scores + slot * kSlotBytes + consumer * 64; #pragma unroll for (int column = 0; column < 64; ++column) { const uint8_t expected = static_cast((epoch + consumer * 17 + column) & 255); const uint8_t actual = row_slot[column]; checksum += actual; if (actual != expected) atomicAdd(reinterpret_cast(result + blockIdx.x * 4 + 2), 1ULL); } const int kv_index = consumer; const uint8_t expected_k = static_cast((epoch + kv_index * 3) & 255); const uint8_t expected_v = static_cast((epoch + kv_index * 5) & 255); const uint8_t actual_k = k_slot[slot * kKvSlotBytes + kv_index]; const uint8_t actual_v = v_slot[slot * kKvSlotBytes + kv_index]; if (actual_k != expected_k || actual_v != expected_v) { atomicAdd(reinterpret_cast(result + blockIdx.x * 4 + 2), 1ULL); } checksum += actual_k + actual_v; } completed_epoch = epoch; __threadfence_block(); if constexpr (UseMbarrier) (void)barriers[2 + slot].arrive(); else free_arrive(slot); } } __syncthreads(); if (tid == 0) result[blockIdx.x * 4] = completed_epoch + 1; if (tid == kProducerThreads) result[blockIdx.x * 4 + 1] = checksum; if (!producer && completed_epoch == epochs - 1) { atomicAdd(reinterpret_cast(result + blockIdx.x * 4 + 3), 1ULL); } } __device__ __forceinline__ void int8_mma(int32_t* c, const uint32_t* a, const uint32_t* b) { asm volatile( "mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 " "{%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%10,%11,%12,%13};" : "=r"(c[0]), "=r"(c[1]), "=r"(c[2]), "=r"(c[3]) : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]), "r"(c[0]), "r"(c[1]), "r"(c[2]), "r"(c[3])); } __device__ __forceinline__ void fp8_mma(uint32_t* c, const uint32_t* a, const uint32_t* b) { asm volatile( "mma.sync.aligned.m16n8k32.row.col.f16.e4m3.e4m3.f16 " "{%0,%1},{%2,%3,%4,%5},{%6,%7},{%8,%9};" : "=r"(c[0]), "=r"(c[1]) : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b[0]), "r"(b[1]), "r"(c[0]), "r"(c[1])); } __global__ __launch_bounds__(kThreads, 1) void vea_b_tensor_issue_probe(int64_t* values, int64_t* clocks, int iterations, int mode) { const int warp = threadIdx.x >> 5; const int lane = threadIdx.x & 31; const bool run_int = warp >= 2 && warp <= 5 && mode != 2; const bool run_fp = warp >= 6 && warp <= 9 && mode != 1; uint32_t a[4] = {0x01010101u + static_cast(lane), 0x01010101u, 0x01010101u, 0x01010101u}; uint32_t b[2] = {0x01010101u, 0x01010101u + static_cast(lane)}; int32_t ci[4] = {0, 0, 0, 0}; uint32_t cf[2] = {0, 0}; const uint64_t started = clock64(); if (run_int) { for (int index = 0; index < iterations; ++index) int8_mma(ci, a, b); } else if (run_fp) { a[0] = a[1] = a[2] = a[3] = 0x38383838u; b[0] = b[1] = 0x38383838u; for (int index = 0; index < iterations; ++index) fp8_mma(cf, a, b); } const uint64_t finished = clock64(); const int offset = (blockIdx.x * 10 + warp); if (lane == 0) { clocks[offset * 2] = static_cast(started); clocks[offset * 2 + 1] = static_cast(finished); values[offset] = run_int ? static_cast(ci[0]) : (run_fp ? static_cast(cf[0]) : 0); } } template __global__ __launch_bounds__(kThreads, 1) void vea_b_register_probe(float* output, int iterations) { const int warp = threadIdx.x >> 5; const int lane = threadIdx.x & 31; float total = 0.0f; if constexpr (Role == 0 || Role == 2) { if (warp >= 2 && warp <= 5) { int32_t rs[32]; float m[8], d[8]; #pragma unroll for (int i = 0; i < 32; ++i) rs[i] = i + lane; #pragma unroll for (int i = 0; i < 8; ++i) { m[i] = i + lane * 0.01f; d[i] = 1.0f + i; } for (int step = 0; step < iterations; ++step) { #pragma unroll for (int i = 0; i < 32; ++i) rs[i] += (step + i) & 7; #pragma unroll for (int i = 0; i < 8; ++i) { m[i] = fmaxf(m[i], step * 0.001f); d[i] = fmaf(d[i], 0.9999f, m[i]); } } #pragma unroll for (int i = 0; i < 32; ++i) total += static_cast(rs[i]); #pragma unroll for (int i = 0; i < 8; ++i) total += m[i] + d[i]; } } if constexpr (Role == 1 || Role == 2) { if (warp >= 6 && warp <= 9) { float ro[128]; #pragma unroll for (int i = 0; i < 128; ++i) ro[i] = i * 0.001f + lane; for (int step = 0; step < iterations; ++step) { #pragma unroll for (int i = 0; i < 128; ++i) ro[i] = fmaf(ro[i], 0.99999f, (step + i) * 0.00001f); } #pragma unroll for (int i = 0; i < 128; ++i) total += ro[i]; } } output[blockIdx.x * kThreads + threadIdx.x] = total; } template std::array attributes(Kernel kernel, int dynamic_shared_bytes) { cudaFuncAttributes value{}; CUDA_CHECK(cudaFuncGetAttributes(&value, kernel)); int active_blocks = 0; CUDA_CHECK(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&active_blocks, kernel, kThreads, dynamic_shared_bytes)); return {value.numRegs, static_cast(value.sharedSizeBytes), static_cast(value.localSizeBytes), value.maxThreadsPerBlock, value.binaryVersion, value.ptxVersion, static_cast(value.maxDynamicSharedSizeBytes), active_blocks}; } } // namespace torch::Tensor launch_handoff_probe(int64_t blocks, int64_t epochs, bool payload) { TORCH_CHECK(blocks > 0 && epochs > 1, "blocks and epochs must be positive"); c10::cuda::CUDAGuard guard(0); auto result = torch::zeros({blocks, 4}, torch::TensorOptions().dtype(torch::kInt64).device(torch::kCUDA)); CUDA_CHECK(cudaFuncSetAttribute(vea_b_handoff_probe, cudaFuncAttributeMaxDynamicSharedMemorySize, kDynamicSharedBytes)); vea_b_handoff_probe<<>>( result.data_ptr(), static_cast(epochs), payload); CUDA_CHECK(cudaGetLastError()); return result; } torch::Tensor launch_mbarrier_handoff_probe(int64_t blocks, int64_t epochs, bool payload) { TORCH_CHECK(blocks > 0 && epochs > 1, "blocks and epochs must be positive"); c10::cuda::CUDAGuard guard(0); auto result = torch::zeros({blocks, 4}, torch::TensorOptions().dtype(torch::kInt64).device(torch::kCUDA)); CUDA_CHECK(cudaFuncSetAttribute(vea_b_handoff_probe, cudaFuncAttributeMaxDynamicSharedMemorySize, kDynamicSharedBytes)); vea_b_handoff_probe<<>>( result.data_ptr(), static_cast(epochs), payload); CUDA_CHECK(cudaGetLastError()); return result; } std::vector launch_tensor_issue_probe(int64_t blocks, int64_t iterations, int64_t mode) { TORCH_CHECK(blocks > 0 && iterations > 0 && mode >= 0 && mode <= 2, "invalid tensor issue arguments"); c10::cuda::CUDAGuard guard(0); auto options = torch::TensorOptions().dtype(torch::kInt64).device(torch::kCUDA); auto values = torch::zeros({blocks, 10}, options); auto clocks = torch::zeros({blocks, 10, 2}, options); vea_b_tensor_issue_probe<<>>( values.data_ptr(), clocks.data_ptr(), static_cast(iterations), static_cast(mode)); CUDA_CHECK(cudaGetLastError()); return {values, clocks}; } torch::Tensor launch_register_probe(int64_t blocks, int64_t iterations, int64_t role) { TORCH_CHECK(blocks > 0 && iterations > 0 && role >= 0 && role <= 2, "invalid register probe arguments"); c10::cuda::CUDAGuard guard(0); auto output = torch::zeros({blocks, kThreads}, torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA)); if (role == 0) vea_b_register_probe<0><<>>(output.data_ptr(), iterations); else if (role == 1) vea_b_register_probe<1><<>>(output.data_ptr(), iterations); else vea_b_register_probe<2><<>>(output.data_ptr(), iterations); CUDA_CHECK(cudaGetLastError()); return output; } torch::Tensor probe_kernel_attributes(int64_t dynamic_shared_bytes) { CUDA_CHECK(cudaFuncSetAttribute( vea_b_handoff_probe, cudaFuncAttributeMaxDynamicSharedMemorySize, static_cast(dynamic_shared_bytes))); CUDA_CHECK(cudaFuncSetAttribute( vea_b_handoff_probe, cudaFuncAttributeMaxDynamicSharedMemorySize, static_cast(dynamic_shared_bytes))); std::array, 6> rows = { attributes(vea_b_handoff_probe, static_cast(dynamic_shared_bytes)), attributes(vea_b_handoff_probe, static_cast(dynamic_shared_bytes)), attributes(vea_b_tensor_issue_probe, 0), attributes(vea_b_register_probe<0>, 0), attributes(vea_b_register_probe<1>, 0), attributes(vea_b_register_probe<2>, 0), }; auto output = torch::empty({6, 8}, torch::TensorOptions().dtype(torch::kInt64)); auto accessor = output.accessor(); for (int row = 0; row < 6; ++row) for (int column = 0; column < 8; ++column) accessor[row][column] = rows[row][column]; return output; }