Prhub

#35604 [CPU] Add native CPU kernel for MurmurHash32

原始 PR 作者 Yuxingwang-intel 合并时间 2026-09-07 09:10 文件变更 4 提交数 22 评论 11 代码增减 +195 / -1

执行摘要

为 MurmurHash32 添加原生 CPU 内核实现与完整测试。

此 PR 是一个有价值的功能补充,实现了标准算法的原生 CPU 支持。核心变更(C++ 内核)逻辑清晰,测试覆盖了关键场景(正确性、形状),并且与项目架构对齐。建议 值得精读,尤其是 sampling.cpp 中的内核实现和 murmur_hash.py 中的调度逻辑,它们展示了为现有函数增加平台后端支持的完整模式。主要关注点是确保实现与已有(如有)GPU 版本或其他实现的行为一致。

讨论亮点

核心讨论集中在实现方案的演进上。作者最初提交了一个纯 Python 的 PyTorch 回退实现。维护者 mingfeima 明确建议:“prefer to move these into sgl-kernel and provide a C++ interface.” 这推动了方案从便捷的 Python 实现转向性能更优、与项目现有内核架构一致的 C++ 原生实现。后续讨论中,mingfeima 还指出了输入检查的重要性(“the implementation expects that inputs are contiguous in memory, add CHECK_INPUT...”),以及后续因 CI 命名变更(PR#37395)导致的测试注册冲突,需要作者进行 rebase 和更新。

实现拆解

  1. 核心 C++ 内核实现:新增 python/sglang/kernels/aot/csrc/cpu/sampling.cpp 文件。在其中实现了 murmur_hash32_cpu 函数及其内部的并行化内核 murmur_hash32_kernel_impl。该内核遵循标准 MurmurHash3 算法,对输入的 seed、positions 和 col_indices 进行混合哈希计算。函数使用 at::parallel_for 实现了数据并行,以提升在多核 CPU 上的性能。变更还包括添加了必要的输入类型检查 (CHECK_INPUT, CHECK_DIM) 和数据维度校验。
  2. PyTorch 算子注册:修改 python/sglang/kernels/aot/csrc/cpu/torch_extension_cpu.cpp。在 TORCH_LIBRARY_FRAGMENT 中添加了对 murmur_hash32_cpu 的函数声明和实现注册(m.def 和 m.impl),使其成为可通过 torch.ops.sgl_kernel 调用的 CPU 算子。
  3. Python 调度入口更新:修改 python/sglang/kernels/ops/sampling/murmur_hash.py。在 murmur_hash32 函数入口处增加了设备判断逻辑:当输入张量在 CPU 上时,导入 sgl_kernel 并直接调用注册的 C++ 算子 torch.ops.sgl_kernel.murmur_hash32_cpu.default,从而绕过可能存在的 Python 路径,确保使用高性能的原生内核。同时移除了原先为 CPU 准备的纯 Python 实现(_murmur_hash32_torch 等函数)。
  4. 单元测试补充:新增 test/registered/cpu/test_sampling.py。包含两个参数化测试用例:test_murmur_hash32_cpu_known_values 验证函数在已知输入下输出固定且正确的哈希值;test_murmur_hash32_cpu_shape_and_dtype 验证输出张量的形状、设备类型和数据类型正确无误。测试文件还通过 register_cpu_ci 注册到 Intel CPU 的 CI 测试流程中。
文件 模块 状态 重要度
python/sglang/kernels/aot/csrc/cpu/sampling.cpp CPU 内核 added 7.75
python/sglang/kernels/ops/sampling/murmur_hash.py 调度入口 modified 4.89
test/registered/cpu/test_sampling.py CPU 测试 added 6.1
python/sglang/kernels/aot/csrc/cpu/torch_extension_cpu.cpp 算子注册 modified 5.59

关键符号

murmur_hash32 murmur_hash32_cpu murmur_hash32_kernel_impl murmur3_mix fmix32 rotl32

关键源码片段

python/sglang/kernels/aot/csrc/cpu/sampling.cpp core-logic

核心变更文件,包含了 MurmurHash32 算法的完整 C++ CPU 内核实现,是本次 PR 的价值所在。

// C++ 内核:实现了 MurmurHash32 算法,并使用 at::parallel_for 进行数据并行处理。
// 输入:种子、位置和列索引的张量指针,以及输出矩阵的维度 (n, m)。
// 核心逻辑:对每个 ( 行 , 列 ) 元素,混合其对应的种子、位置和列索引值,生成一个 32 位哈希值。
template <typename pos_t>
void murmur_hash32_kernel_impl(
    const uint64_t* seed_ptr,
    const pos_t* positions_ptr,
    const int64_t* col_indices_ptr,
    uint32_t* output_ptr,
    int64_t n,
    int64_t m) {
  const int64_t total = n * m;
  // 使用 ATen 的并行 for 循环,自动根据系统 CPU 核心数分块执行。
  at::parallel_for(0, total, 0, [&](int64_t begin, int64_t end) {
    for (int64_t idx = begin; idx < end; ++idx) {
      // 通过线性索引计算矩阵的行列坐标。
      const int64_t row = idx / m;
      const int64_t col_idx = idx % m;
      const uint64_t seed = seed_ptr[row];
      // 将位置转换为 32 位整数。
      const uint32_t pos = static_cast<uint32_t>(positions_ptr[row]);
      const uint32_t col = static_cast<uint32_t>(col_indices_ptr[col_idx]);
      // 将 64 位种子拆分为两个 32 位块进行处理。
      const uint32_t seed_low = static_cast<uint32_t>(seed);
      const uint32_t seed_high = static_cast<uint32_t>(seed >> 32);
      uint32_t h = 0;
      // 依次混合种子低部、种子高部、位置和列索引。
      h = murmur3_mix(h, seed_low);
      h = murmur3_mix(h, seed_high);
      h = murmur3_mix(h, pos);
      h = murmur3_mix(h, col);
      h ^= 16u; // 最终长度混合
      h = fmix32(h); // 雪崩混合
      output_ptr[idx] = h;
    }
  });
}
python/sglang/kernels/ops/sampling/murmur_hash.py infrastructure

Python 层的调度入口,根据设备类型决定调用原生 C++ 内核还是其他路径,是连接用户接口与底层实现的关键桥梁。

def murmur_hash32(seed, positions, col_indices):
    """MurmurHash32 implementation for token hashing."""
    assert len(seed.shape) == 1 and len(col_indices.shape) == 1, (
        f"Inputs must be 1D tensors {seed.shape=} {col_indices.shape=}"
    )
    # 新增:设备类型检查,为 CPU 路径提供原生内核。
    device = seed.device
    if device.type == "cpu":
        import sgl_kernel # noqa: F401
        # 直接调用在 C++ 中注册并实现的算子。
        return torch.ops.sgl_kernel.murmur_hash32_cpu.default(
            seed, positions, col_indices
        )
    n = seed.shape[0]
    m = col_indices.shape[0]
    # ... 后续 GPU 或其他设备的原有逻辑 ...
test/registered/cpu/test_sampling.py test-coverage

为新增的 CPU 内核提供测试覆盖,验证算法正确性和基本接口行为,是确保质量的关键。

import torch
from sglang.kernels.ops.sampling.murmur_hash import murmur_hash32
​
# 测试 1:使用已知的输入和预期的哈希输出值进行精确验证。
@pytest.mark.parametrize("positions_dtype", [torch.int64, torch.uint64])
def test_murmur_hash32_cpu_known_values(positions_dtype):
    seed = torch.tensor([0, 1, 42, 0x123456789ABCDEF0], dtype=torch.uint64)
    positions = torch.tensor([0, 7, 123, 456], dtype=positions_dtype)
    col_indices = torch.tensor([0, 1, 2, 17], dtype=torch.int64)
    actual = murmur_hash32(seed, positions, col_indices)
    # 预期值基于标准 MurmurHash3 算法计算得出。
    expected = torch.tensor(
        [
            [2167721464, 10027521, 2423355346, 2203067026],
            [3755322398, 4196701286, 1002451629, 183234019],
            [772287619, 548237471, 2740678348, 3656549299],
            [3746406971, 2891010872, 104055988, 3372550890],
        ],
        dtype=torch.uint32,
    )
    torch.testing.assert_close(actual, expected)
​
# 测试 2:验证输出张量的设备类型、数据类型和形状。
@pytest.mark.parametrize("positions_dtype", [torch.int64, torch.uint64])
def test_murmur_hash32_cpu_shape_and_dtype(positions_dtype):
    seed = torch.tensor([1, 2, 3], dtype=torch.uint64)
    positions = torch.tensor([10, 20, 30], dtype=positions_dtype)
    col_indices = torch.arange(128, dtype=torch.int64)
    actual = murmur_hash32(seed, positions, col_indices)
    assert actual.device.type == "cpu"
    assert actual.dtype == torch.uint32
    assert actual.shape == (3, 128)

评论区精华

从 Python 回退方案到 C++ 原生内核的方案变更 设计

作者最初提交了纯 Python 的 PyTorch 回退实现。维护者 mingfeima 建议“prefer to move these into sgl-kernel and provide a C++ interface”,推动实现转向与项目架构更一致的高性能 C++ 内核。

结论:作者采纳了建议,将实现从 Python 迁移到 C++ 并集成了原生内核。 · 已解决

C++ 内核的输入检查 正确性

mingfeima 指出“the implementation expects that inputs are contiguous in memory, add `CHECK_INPUT` for the input tensors. util also has CHECK_DIM”,要求增强输入参数的健壮性检查。

结论:作者在 `murmur_hash32_cpu` 函数中添加了 `CHECK_INPUT` 和 `CHECK_DIM` 宏调用,并对数据类型和维度进行了 `TORCH_CHECK`。 · 已解决

CI 测试注册命名冲突 other

由于 PR#37395 重命名了 Xeon CI 的测试注册套件,导致本 PR 的测试注册名需要更新,产生了合并冲突。

结论:维护者 mingfeima 指导作者在 PR#37395 合并后 rebase 并更新测试注册名。 · 已解决

风险与影响

主要风险在于 算法正确性。新增的 C++ 内核实现必须与标准的 MurmurHash3 算法 bit-for-bit 一致。PR 中通过 test_murmur_hash32_cpu_known_values 测试用例,使用硬编码的预期值进行验证,这有效降低了风险。另一个风险是 输入假设。内核实现假设输入张量是内存连续的(由 CHECK_INPUT 宏保证),如果传入非连续张量可能引发未定义行为或性能下降,但当前的输入检查已覆盖此情况。此外,at::parallel_for 的使用可能在极端小数据量时引入微小开销,但鉴于这是针对多核 CPU 设计的,影响可忽略。

此变更对 用户 几乎透明,仅影响在 CPU 设备上调用 murmur_hash32 函数的路径。对于依赖该哈希函数的 CPU 采样逻辑,理论上能获得更低的延迟和更高的吞吐。对 系统 而言,它扩展了 sgl-kernel 的 CPU 算子库,增强了跨平台一致性。对 团队 来说,这引入了一个符合项目模式的 CPU 内核开发范例,但同时增加了内核模块的维护点。

算法正确性依赖测试覆盖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论