FW-GLM-5-1 · popcorn · pop_l123_st_gpt
compile_fail 13_QuantVLA_SelectiveLayoutFusion.py
model
FW-GLM-5-1
turns / max
1 / 1
tool calls
1
tokens (total)
5044
wall clock
42.4s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
-1.00 μs
speedup
—
started
2026-05-11T09:25:06Z
compile error: /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/FW-GLM-5-1/level_2_problem_13_cache/k_f958544855b0/fused_quant_blend_linear/fused_quant_blend_linear.so: cannot open shared object file: No such file or directory
timingkernelbench.timing
kernel runtime
—
ref runtime
—
speedup
—
kernel mean / median / std
—
ref mean / median / std
—
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

# Default ModelNew is PyTorch-only. run_and_check: use check_kernel=False with backend=cuda
# until the candidate file contains a real CUDA kernel (see kernel_static_checker).


def _fake_quant_symmetric_int8(x: torch.Tensor, scale: torch.Tensor) -> torch.Tensor:
    """Symmetric int8 fake-quant: round(x/scale) clamped to [-127, 127], times scale."""
    q = torch.clamp(torch.round(x / scale), -127.0, 127.0)
    return q * scale


class Model(nn.Module):
    """
    **QuantVLA selective layout fusion** (reference target for a fused CUDA/Triton kernel).

    Vision–language–action stacks often use **token-wise mixed precision**: some
    timesteps or modalities tolerate aggressive quantization while others stay in
    full precision. This module fuses in **one forward**:

      1. **Layout routing** — ``sigmoid(layout_logits)`` yields a per-token mix
         coefficient in ``(0, 1)`` (quant vs FP residual).
      2. **Quantitative path** — global symmetric **int8-style fake quantization**
         (scale from the tensor max, ``/ 127``).
      3. **Selective blend** — ``α · x_quant + (1 - α) · x``.
      4. **Head** — linear projection on the mixed activations.

    This matches a “quantitative selective layout” block: the layout logits (from a
    router, modality tags, or calibration) choose how strongly each position uses the
    quantized layout vs the identity layout.
    """

    def __init__(self, dim: int):
        super().__init__()
        self.dim = dim
        self.out_proj = nn.Linear(dim, dim, bias=True)

    def forward(self, x: torch.Tensor, layout_logits: torch.Tensor) -> torch.Tensor:
        """
        Args:
            x: (B, T, D) VLA token activations (fused modalities).
            layout_logits: (B, T) unbounded logits; larger → more mass on quantized path.
        Returns:
            (B, T, D) updated activations.
        """
        B, T, D = x.shape
        assert D == self.dim
        assert layout_logits.shape == (B, T)

        alpha = torch.sigmoid(layout_logits).unsqueeze(-1)
        with torch.no_grad():
            amax = x.detach().abs().max().clamp(min=1e-8)
        scale = amax / 127.0
        x_q = _fake_quant_symmetric_int8(x, scale)
        mixed = alpha * x_q + (1.0 - alpha) * x
        return self.out_proj(mixed)


class ModelNew(nn.Module):
    """KernelBench candidate entry point; default delegates to ``Model``."""

    def __init__(self, dim: int):
        super().__init__()
        self._impl = Model(dim)

    def forward(self, x: torch.Tensor, layout_logits: torch.Tensor) -> torch.Tensor:
        return self._impl(x, layout_logits)


batch_size = 8
seq_len = 64
dim = 128


def get_inputs():
    p = popcorn_pri
    x = torch.randn(p.jitter_int(batch_size), p.jitter_int(seq_len), dim)
    layout_logits = torch.randn(p.jitter_int(batch_size), p.jitter_int(seq_len))
    return [x, layout_logits]


def get_init_inputs():
    return [dim]
turn 0 · FINAL · 1 tool call(s) · 42.4s turn · 39.5s LLM · tokens in=1,549 · out=3,495 · total=5,044 · 16,340 chars (click to expand)
turn 042.4s turn · 39.5s LLM · 2.9s tools · tokens in=1,549 · out=3,495 · total=5,044 · FINAL
reasoning
Let me analyze this task. I need to create a custom CUDA kernel that replaces the PyTorch model's forward computation. The model does: 1. `alpha = torch.sigmoid(layout_logits).unsqueeze(-1)` - sigmoid of layout_logits, shape (B, T, 1) 2. `amax = x.detach().abs().max().clamp(min=1e-8)` - find max absolute value in x, clamped 3. `scale = amax / 127.0` - compute scale for int8 quantization 4. `x_q = _fake_quant_symmetric_int8(x, scale)` - fake quantize: round(x/scale) clamped to [-127, 127], then * scale 5. `mixed = alpha * x_q + (1.0 - alpha) * x` - blend quantized and original 6. `return self.out_proj(mixed)` - linear projection So the fused kernel should: 1. Compute sigmoid of layout_logits 2. Compute amax (reduction across entire x tensor) 3. Compute scale 4. For each element: compute quantized value, blend with original based on alpha 5. Then apply linear projection (matrix multiplication + bias) The linear projection at the end is a standard GEMM operation. I could either: - Fuse everything including the linear projection - Fuse steps 1-4 and use PyTorch for the linear projection Let me think about fusing everything into one kernel. The linear projection is `(B*T, D) @ (D, D) + bias`, which is a GEMM. This is typically well-optimized by cuBLAS, so fusing it might not give much benefit. However, fusing the element-wise operations (sigmoid, quantization, blending) into one kernel could save memory bandwidth. Let me write a kernel that: 1. Computes amax using a reduction kernel 2. Fuses sigmoid, quantization, and blending into one kernel 3. Uses PyTorch for the final linear projection Actually, for better performance, let me try to fuse as much as possible: - Compute amax in a reduction pass - Fuse sigmoid + quantization + blending + output into one kernel, storing results in a temporary buffer - Apply the linear projection using PyTorch Wait, I could actually fuse everything into one kernel by storing the mixed values in shared memory and then having each thread apply its portion of the linear projection. But that gets complicated with the D×D matrix multiplication. A more practical approach: use a reduction for amax, then a single kernel that handles sigmoid, quantization, blending, and the linear projection all together. For the linear projection, each thread could compute a dot product with the weight matrix. Actually, the cleanest strategy is keeping it simple: one pass to compute amax, another pass that fuses sigmoid + quantization + blending, then leverage cuBLAS for the linear projection since it's already highly optimized for this operation. I'll create a kernel file with the quantization and blending logic, then integrate it with PyTorch's GEMM for the final projection. ], D); } } } The kernel handles the quantized transformation by computing sigmoid-based mixing coefficients, then blending between quantized and original values before projecting to the output dimension. Each thread processes multiple input elements to maximize throughput, with the mixed values stored in shared memory before the final projection step. I need to reconsider the approach. Using cuBLAS for the linear layer might be more efficient, and I should verify the quantization logic matches the reference implementation—specifically computing the scale from the global maximum absolute value rather than per-token statistics. The flow requires computing the scale factor from the maximum across all tokens, then quantizing each value by rounding, clamping to [-127, 127], and scaling back. I'll launch one kernel to extract the global maximum, another to handle the sigmoid computation and blending, then leverage PyTorch's optimized linear layer for the final projection. The scale is a scalar derived from the maximum value across the entire tensor. I'll implement this with a two-kernel approach: first computing the global max via reduction, then applying the fused operations. For the reduction, a simple atomic max approach avoids custom kernel complexity, though I should consider optimization tradeoffs since atomic operations can be costly on large tensors. An alternative would be using thrust or cub libraries, but that introduces dependencies. Instead, I'll create a reduction kernel that computes the maximum absolute value efficiently by processing data in tiles and maintaining partial maxima in shared memory. The kernel loads chunks of the input tensor, computes absolute values, and threads cooperate to find the maximum across their assigned elements, ultimately writing the result to global memory. For the second kernel, I apply the sigmoid-based quantization and blending operation. This kernel takes the layout logits, input tensor, and pre-computed scale, then produces the mixed output. The challenge is handling different tensor shapes—the layout logits have dimensions (B, T) while the input x has dimensions (B, T, D), so I need to map the per-token alpha values appropriately across the feature dimension. The complete implementation combines both kernels: a reduction kernel to compute the maximum absolute value across all elements, followed by a blending kernel that applies the sigmoid transformation and quantization scheme. The host function orchestrates this by launching the first kernel to get the global scale factor, then using that scale in the second kernel to produce the final mixed output. _proj.weight bias = self.out_proj.bias mixed = quant_blend_cuda(x, layout_logits, weight, bias) return mixed The implementation needs to handle the quantization and blending steps in CUDA while delegating the linear projection to the fused kernel. I'm defining the extension with inline CUDA code that includes a clamp helper and a kernel for computing the maximum absolute value across the input tensor. The reduction kernel processes elements with a stride loop, tracking the maximum absolute value encountered. For quantization and blending, a second kernel applies sigmoid to layout logits, then uses that to blend between quantized and original values. Each thread handles multiple output elements, computing per-token alpha values from the layout logits and applying the quantization scale before blending with the original input. The blended results are stored back to global memory for the subsequent linear projection. The main function handles dimension checks and determines grid/block dimensions based on the batch size and sequence length. It then allocates output tensors and launches the quant_blend kernel with the appropriate parameters, using streams to synchronize execution before returning the result. However, I'm reconsidering whether fusing the linear layer with the quantization operation is actually beneficial. The quantization produces a (B, T, D) tensor, which then gets reshaped to (B*T, D) for the GEMM operation—this is a standard matrix multiplication that cuBLAS already handles efficiently. While fused kernels reduce memory bandwidth for element-wise operations, the GEMM itself is compute-bound rather than memory-bound, so the performance gain from fusion might not be significant. A simpler approach would be to keep the quantization and blending separate from the linear layer, leveraging PyTorch's optimized GEMM implementation instead. _quant_symmetric_int8(x, scale) mixed = alpha * x_q + (1.0 - alpha) * x return self.out_proj(mixed) ``` This approach is too slow since it relies on PyTorch operations. I need to fuse the sigmoid, quantization, and blending into a single CUDA kernel for efficiency. The kernel should apply sigmoid to layout_logits, compute quantized values, blend them with the original input, and handle the linear projection in one pass. Each thread processes multiple elements across the sequence dimension. For each batch index and sequence position, I compute alpha from the sigmoid of layout_logits, then iterate through the embedding dimension to quantize, blend, and apply the linear transformation. The weight matrix and bias are loaded from global memory, and the result is written back to the output tensor. amax = torch.max(torch.abs(x)).clamp(min=1e-8) scale = amax / 127.0 B, T, D = x.shape output = torch.empty(B, T, D, dtype=x.dtype, device=x.device) grid = (B * T,) block = 256 quant_blend_linear_kernel<<<grid, block>>>( x.data_ptr<float>(), layout_logits.data_ptr<float>(), self.weight.data_ptr<float>(), self.bias.data_ptr<float>(), output.data_ptr<float>(), scale.item(), B, T, D ); return output This approach reduces the quantization scale computation to a single tensor operation, then launches a fused kernel that handles sigmoid, quantization, blending, and the linear projection in one pass. The kernel processes all elements across batch and sequence dimensions, with each thread handling multiple feature dimensions to maximize throughput.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Compile the CUDA kernel inline at module load time\n_fused_kernel = load_inline(\n name='fused_quant_blend_linear',\n cpp_sources=r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void quant_blend_linear_kernel(\n const float* __restrict__ x,\n const float* __restrict__ layout_logits,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n float scale,\n int B, int T, int D\n) {\n int bt = blockIdx.x;\n int b = bt / T;\n int t = bt % T;\n int tid = threadIdx.x;\n \n if (b >= B || t >= T) return;\n \n // Compute sigmoid for this token\n float alpha = 1.0f / (1.0f + expf(-layout_logits[bt]));\n \n // Pre-compute quantize, blend, and linear for each D element\n // Each thread handles multiple D elements\n for (int d = tid; d < D; d += blockDim.x) {\n float x_val = x[b * T * D + t * D + d];\n \n // Fake quant: round(x/scale) clamped to [-127, 127], times scale\n float q = roundf(x_val / scale);\n q = fmaxf(-127.0f, fminf(127.0f, q));\n float x_q = q * scale;\n \n // Blend\n float mixed = alpha * x_q + (1.0f - alpha) * x_val;\n \n // Linear projection\n float sum = bias[d];\n for (int k = 0; k < D; ++k) {\n float m = (k < d) ? \n ((k < tid) ? x[b * T * D + t * D + k] : __ldg(&x[b * T * D + t * D + k])) :\n __ldg(&x[b * T * D + t * D + k]);\n }\n \n // Actually compute linear: output[d] = sum_k(mixed[k] * weight[k, d]) + bias[d]\n sum = bias[d];\n for (int k = 0; k < D; ++k) {\n float m_k;\n if (k == d && tid == d % blockDim.x) {\n // Use local mixed value\n m_k = mixed;\n } else {\n // Need to compute mixed[k] or reload\n float x_k = __ldg(&x[b * T * D + t * D + k]);\n float q_k = roundf(x_k / scale);\n q_k = fmaxf(-127.0f, fminf(127.0f, q_k));\n float x_q_k = q_k * scale;\n m_k = alpha * x_q_k + (1.0f - alpha) * x_k;\n }\n sum += m_k * __ldg(&weight[k * D + d]);\n }\n output[b * T * D + t * D + d] = sum;\n }\n}\n\n// Optimized version: compute mixed values first in shared memory\n__global__ void quant_blend_linear_kernel_opt(\n const float* __restrict__ x,\n const float* __restrict__ layout_logits,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ output,\n float scale,\n int B, int T, int D\n) {\n extern __shared__ float mixed_shared[];\n \n int bt = blockIdx.x;\n int b = bt / T;\n int t = bt % T;\n int tid = threadIdx.x;\n \n if (b >= B || t >= T) return;\n \n // Compute sigmoid for this token (same for all d)\n float alpha = 1.0f / (1.0f + expf(-layout_logits[bt]));\n \n // Load x, quantize, blend, store to shared memory\n for (int d = tid; d < D; d += blockDim.x) {\n float x_val = __ldg(&x[b * T * D + t * D + d]);\n float q = roundf(x_val / scale);\n q = fmaxf(-127.0f, fminf(127.0f, q));\n float x_q = q * scale;\n mixed_shared[d] = alpha * x_q + (1.0f - alpha) * x_val;\n }\n __syncthreads();\n \n // Linear projection: each thread computes one output element\n for (int d_out = tid; d_out < D; d_out += blockDim.x) {\n float sum = bias[d_out];\n for (int k = 0; k < D; ++k) {\n sum += mixed_shared[k] * __ldg(&weight[k * D + d_out]);\n }\n output[b * T * D + t * D + d_out] = sum;\n }\n}\n\ntorch::Tensor fused_quant_blend_linear_cuda(\n torch::Tensor x,\n torch::Tensor layout_logits,\n torch::Tensor weight,\n torch::Tensor bias\n) {\n TORCH_CHECK(x.dim() == 3, \"x must be 3D\");\n TORCH_CHECK(layout_logits.dim() == 2, \"layout_logits must be 2D\");\n \n int B = x.size(0);\n int T = x.size(1);\n int D = x.size(2);\n \n TORCH_CHECK(layout_logits.size(0) == B, \"layout_logits batch mismatch\");\n TORCH_CHECK(layout_logits.size(1) == T, \"layout_logits seq mismatch\");\n \n // Compute global amax and scale on CPU (fast for single value)\n float amax = x.abs().max().item<float>();\n amax = fmaxf(amax, 1e-8f);\n float scale = amax / 127.0f;\n \n auto output = torch::empty({B, T, D}, x.options());\n \n int grid = B * T;\n int block = (D + 31) / 32 * 32; // Round up to warp size\n block = min(block, 1024);\n size_t shared_mem = D * sizeof(float);\n \n quant_blend_linear_kernel_opt<<<grid, block, shared_mem>>>(\n x.data_ptr<float>(),\n layout_logits.data_ptr<float>(),\n weight.data_ptr<float>(),\n bias.data_ptr<float>(),\n output.data_ptr<float>(),\n scale,\n B, T, D\n );\n \n return output;\n}\n''',\n functions=['fused_quant_blend_linear_cuda'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"Fused CUDA kernel for quant-blend-linear operation.\"\"\"\n\n def __init__(self, dim: int):\n super().__init__()\n self.dim = dim\n # Create linear layer with parameters\n self.out_proj = nn.Linear(dim, dim, bias=True)\n\n def forward(self, x: torch.Tensor, layout_logits: torch.Tensor) -> torch.Tensor:\n B, T, D = x.shape\n assert D == self.dim\n assert layout_logits.shape == (B, T)\n \n # Use fused CUDA kernel for the entire computation\n return _fused_kernel.fused_quant_blend_linear_cuda(\n x, layout_logits, self.out_proj.weight, self.out_proj.bias\n )" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: kernel did not compile. /scratch/abaner/KernelBench/runs/pop_l123_st_gpt/popcorn/FW-GLM-5-1/level_2_problem_13_cache/k_f958544855b0/fused_quant_blend_linear/fused_quant_blend_linear.so: cannot open shared object file: No such file or directory
final submitted kernel (181 lines, 5,752 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

# Compile the CUDA kernel inline at module load time
_fused_kernel = load_inline(
    name='fused_quant_blend_linear',
    cpp_sources=r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cmath>

__global__ void quant_blend_linear_kernel(
    const float* __restrict__ x,
    const float* __restrict__ layout_logits,
    const float* __restrict__ weight,
    const float* __restrict__ bias,
    float* __restrict__ output,
    float scale,
    int B, int T, int D
) {
    int bt = blockIdx.x;
    int b = bt / T;
    int t = bt % T;
    int tid = threadIdx.x;
    
    if (b >= B || t >= T) return;
    
    // Compute sigmoid for this token
    float alpha = 1.0f / (1.0f + expf(-layout_logits[bt]));
    
    // Pre-compute quantize, blend, and linear for each D element
    // Each thread handles multiple D elements
    for (int d = tid; d < D; d += blockDim.x) {
        float x_val = x[b * T * D + t * D + d];
        
        // Fake quant: round(x/scale) clamped to [-127, 127], times scale
        float q = roundf(x_val / scale);
        q = fmaxf(-127.0f, fminf(127.0f, q));
        float x_q = q * scale;
        
        // Blend
        float mixed = alpha * x_q + (1.0f - alpha) * x_val;
        
        // Linear projection
        float sum = bias[d];
        for (int k = 0; k < D; ++k) {
            float m = (k < d) ? 
                ((k < tid) ? x[b * T * D + t * D + k] : __ldg(&x[b * T * D + t * D + k])) :
                __ldg(&x[b * T * D + t * D + k]);
        }
        
        // Actually compute linear: output[d] = sum_k(mixed[k] * weight[k, d]) + bias[d]
        sum = bias[d];
        for (int k = 0; k < D; ++k) {
            float m_k;
            if (k == d && tid == d % blockDim.x) {
                // Use local mixed value
                m_k = mixed;
            } else {
                // Need to compute mixed[k] or reload
                float x_k = __ldg(&x[b * T * D + t * D + k]);
                float q_k = roundf(x_k / scale);
                q_k = fmaxf(-127.0f, fminf(127.0f, q_k));
                float x_q_k = q_k * scale;
                m_k = alpha * x_q_k + (1.0f - alpha) * x_k;
            }
            sum += m_k * __ldg(&weight[k * D + d]);
        }
        output[b * T * D + t * D + d] = sum;
    }
}

// Optimized version: compute mixed values first in shared memory
__global__ void quant_blend_linear_kernel_opt(
    const float* __restrict__ x,
    const float* __restrict__ layout_logits,
    const float* __restrict__ weight,
    const float* __restrict__ bias,
    float* __restrict__ output,
    float scale,
    int B, int T, int D
) {
    extern __shared__ float mixed_shared[];
    
    int bt = blockIdx.x;
    int b = bt / T;
    int t = bt % T;
    int tid = threadIdx.x;
    
    if (b >= B || t >= T) return;
    
    // Compute sigmoid for this token (same for all d)
    float alpha = 1.0f / (1.0f + expf(-layout_logits[bt]));
    
    // Load x, quantize, blend, store to shared memory
    for (int d = tid; d < D; d += blockDim.x) {
        float x_val = __ldg(&x[b * T * D + t * D + d]);
        float q = roundf(x_val / scale);
        q = fmaxf(-127.0f, fminf(127.0f, q));
        float x_q = q * scale;
        mixed_shared[d] = alpha * x_q + (1.0f - alpha) * x_val;
    }
    __syncthreads();
    
    // Linear projection: each thread computes one output element
    for (int d_out = tid; d_out < D; d_out += blockDim.x) {
        float sum = bias[d_out];
        for (int k = 0; k < D; ++k) {
            sum += mixed_shared[k] * __ldg(&weight[k * D + d_out]);
        }
        output[b * T * D + t * D + d_out] = sum;
    }
}

torch::Tensor fused_quant_blend_linear_cuda(
    torch::Tensor x,
    torch::Tensor layout_logits,
    torch::Tensor weight,
    torch::Tensor bias
) {
    TORCH_CHECK(x.dim() == 3, "x must be 3D");
    TORCH_CHECK(layout_logits.dim() == 2, "layout_logits must be 2D");
    
    int B = x.size(0);
    int T = x.size(1);
    int D = x.size(2);
    
    TORCH_CHECK(layout_logits.size(0) == B, "layout_logits batch mismatch");
    TORCH_CHECK(layout_logits.size(1) == T, "layout_logits seq mismatch");
    
    // Compute global amax and scale on CPU (fast for single value)
    float amax = x.abs().max().item<float>();
    amax = fmaxf(amax, 1e-8f);
    float scale = amax / 127.0f;
    
    auto output = torch::empty({B, T, D}, x.options());
    
    int grid = B * T;
    int block = (D + 31) / 32 * 32;  // Round up to warp size
    block = min(block, 1024);
    size_t shared_mem = D * sizeof(float);
    
    quant_blend_linear_kernel_opt<<<grid, block, shared_mem>>>(
        x.data_ptr<float>(),
        layout_logits.data_ptr<float>(),
        weight.data_ptr<float>(),
        bias.data_ptr<float>(),
        output.data_ptr<float>(),
        scale,
        B, T, D
    );
    
    return output;
}
''',
    functions=['fused_quant_blend_linear_cuda'],
    extra_cuda_cflags=['-O3', '--use_fast_math'],
    verbose=False
)


class ModelNew(nn.Module):
    """Fused CUDA kernel for quant-blend-linear operation."""

    def __init__(self, dim: int):
        super().__init__()
        self.dim = dim
        # Create linear layer with parameters
        self.out_proj = nn.Linear(dim, dim, bias=True)

    def forward(self, x: torch.Tensor, layout_logits: torch.Tensor) -> torch.Tensor:
        B, T, D = x.shape
        assert D == self.dim
        assert layout_logits.shape == (B, T)
        
        # Use fused CUDA kernel for the entire computation
        return _fused_kernel.fused_quant_blend_linear_cuda(
            x, layout_logits, self.out_proj.weight, self.out_proj.bias
        )