开源项目
开源项目

DeepSelect:DeepSeek 为 DSA 打造的高性能 TopK 算子

DeepSeek 官方组织于 2026-09-10 开源 DeepSelect(GitHub 快照:152 星 / CUDA / 当日创建并活跃),这是 DeepSeek Sparse Attention(DSA)所用 TopK kernel 的高性能实现与配套 sampler;DSA 应用于 V3.2、V4、V4.1 三代模型,官方称相比原生 torch.topk 取得 2~20 倍加速,同日发布 v1.0.0 与中英双语算法深度解析文档。本文拆解为什么 TopK 会成为注意力瓶颈、RadixSelect 的「一遍扫描 + 随机块 + 阈值收敛」思路、「总处理元素数期望上界」意味着什么,以及为什么该用有效内存带宽而非 FLOP 来度量性能;重点写清它不做什么:仅支持 Lightning Indexer(bfloat16、topk 必须 ≤4096)与 Sampling(float32、vocab 约 128K)两类 workload,官方调参建议是关闭 sorted_index、不需要取值时设 return_value=False。冷思考:152 星说明项目极早期,对 DeepSeek 系模型直接有用,生态价值大于通用价值。

发布于 2026年9月10日10 分钟阅读
<!-- deepseek-deepselect-resource | open-source | DeepSelect:DeepSeek 为 DSA 打造的高性能 TopK 算子 -->

2026 年 9 月 10 日,DeepSeek 官方组织在 GitHub 上发布了仓库 deepseek-ai/DeepSelect,同一天还放出了 v1.0.0 与一份中英双语的算法深度解析文档。截至发布当天,仓库拿到 152 颗星,主语言是 CUDA,创建并首次推送都发生在 9 月 10 日,属于当日活跃的新项目。它不是又一个"又训了个模型"的故事,而是一个更底层、也更容易被忽视的东西:高性能 TopK 算子与其配套的采样器。

值得注意的呼应在同一天发生。DeepSeek 同日开源了 V4.1 Flash,而 DeepSeek Sparse Attention(简称 DSA,下文统一用 DSA)正是 V4.1 Flash 底层的稀疏注意力机制——本库就是 DSA 所依赖的算子底座。换句话说,你看到的"模型开源"只是水面上的那部分,DeepSelect 是水面下让稀疏注意力跑得动、跑得快的算子实现。本批我们同时发了一篇 V4.1 Flash 的热点解读(点此阅读),可以把两篇对照着看:那篇讲"模型对外是什么",这篇讲"它靠什么算子撑起来"。

一、项目是什么:DSA 稀疏注意力的 TopK 算子底座

先说清楚 DeepSelect 的定位。它不是一个通用 TopK 库,而是「DeepSeek Sparse Attention(DSA)所用 TopK kernel 的高性能实现 + sampler」。DSA 被用在 DeepSeek V3.2、DeepSeek V4 和 DeepSeek V4.1 三代模型里。在 DSA 内部,需要用 TopK 算子从大量 context token 中选出最相关的位置;在采样阶段,也需要用 TopK 从一个较大的词表里选出候选 token。DeepSelect 干的事,就是为这两类「选最大的 k 个」需求提供比原生 torch.topk 快得多的实现,官方给出的实测加速区间是 2 到 20 倍(README 原文 "2 ~ 20x speedup")。

把关系捋一遍:用户调用的是 DeepSeek 的模型(V3.2 / V4 / V4.1),模型内部跑 DSA 做稀疏注意力,DSA 内部频繁调用 TopK,而 DeepSelect 就是把这一层 TopK 换成了自己手写的高性能 CUDA kernel。所以 DeepSelect 的价值首先体现在「它让 DeepSeek 系模型在推理时更省算力、更快」,而不是直接给用户一个能随便套在任何任务上的 TopK 工具。

这也是为什么我们对它的判断要冷静:它的第一性是为 DeepSeek 系模型服务。这一点放到第四节和第六节再展开。

二、为什么 TopK 会卡住注意力:被忽视的瓶颈

很多做工程的同学对注意力的直觉集中在「矩阵乘」上,以为瓶颈永远是 GEMM。但在稀疏注意力的语境下,TopK 反而可能成为端到端延迟里不可忽略的一块。原因很直接:DSA 要从一长串 context token 里挑出最相关的那几个位置,这个「挑选」动作本身就是一次 TopK;而采样的候选 token 筛选,又是一次对大词表(约 12.8 万量级)的 TopK。这两类操作在每一层、每一步解码里都可能反复发生。

原生 torch.topk 是通用实现,它要为各种 dtype、各种 shape、各种 topk 大小都留余量,常数因子做不到极致。而当 TopK 的输入是一个几十万长度的行、且只需要取其中很小的 k(比如 512)时,通用实现的「把整行排序再取前 k」思路就会做大量无用功。DeepSelect 的出发点正是:既然 DSA 和采样这两类 workload 的输入分布、dtype、topk 大小高度可预期,就可以为它们量身定制算法,把不经济的全排序替换成「一次扫描 + 阈值收敛」的思路,从而吃掉那 2 到 20 倍的加速空间。

这也解释了官方文档反复强调的一点:TopK 的 workload 千差万别,最快的算法与实现高度依赖输入 dtype、batch_sizevocab_sizetopk。DeepSelect 明确只聚焦两类场景,而不是号称「通吃一切」。这种克制,恰恰是它能做快的前提。

三、RadixSelect 算法:一遍扫描 + 随机块 + 阈值收敛

DeepSelect 的算法核心是一个名为 DeepSelectTopk 的流程。它维护一个初值为 -inf 的 top-k 阈值 T,然后循环执行三个简单步骤:

  1. 扫描(Scan):按照随机顺序,每次扫描输入序列中大小为 B 的一块。
  2. 筛选(Filter):根据当前阈值 T 筛选,只把仍然可能进入 top-k 的元素(也就是大于 T 的元素)追加进候选缓冲区 topk_candidate
  3. 压缩(Compact):当候选缓冲区变大(或算法结束时),在 shared memory 里执行一次基于 radix-select 的 TopK(也可以用别的 TopK 算法),把候选集重新精简回 k 个元素,并把 T 更新为这 k 个元素里最小的那个值。

伪代码(原文逐字)是这样的:

text
DeepSelectTopk(x[0:N), k, B, B2):
    require 1 <= k <= N and B >= 1 and B2 >= 1

    topk_candidate = []                     # shared memory
    topk_threshold = -inf                   # current top-k threshold
    p = random_permutation(ceil_div(N, B))  # independent of x

    for block in p:
        lo, hi = block * B, min((block + 1) * B, N)

        # Keep only elements that may still enter the top-k.
        for j in range(lo, hi):
            if x[j] > topk_threshold:
                topk_candidate.append((x[j], j))

        # Periodically compact the candidate buffer.
        if len(topk_candidate) >= k + B2:
            topk_candidate = RadixSelectTopK(topk_candidate, k)
            topk_threshold = min(v for v, _ in topk_candidate)

    return RadixSelectTopK(topk_candidate, k)

初始时阈值是 -inf,所以在第一次压缩之前,所有元素都会被接受。此后 topk_threshold 表示当前第 k 大的元素值,并且随着扫描推进只会单调增大。于是后续元素通过阈值筛选的概率逐渐降低,候选缓冲区的增长速度也越来越慢。文档给出一个对 k=512 较合理的参数配置:B = B2 = 1024

几个工程上很关键的性质:任意时刻都有 len(topk_candidate) <= k + B2 + Bx 里的每个元素恰好被读取一次,而且以大小为 B 的连续块方式访问(这对显存带宽友好);除去随机排列,算法额外所需的空间只有 O(k + B + B2),小到可以放在速度更快的 shared memory 里。

这里还要点出一个设计动机:为了避免极端输入下的性能退化,块的处理顺序必须是随机的。这一点不是拍脑袋,下一节会用期望上界来给它一个严格保证。

四、"总处理元素数期望上界"到底意味着什么

这是整篇算法解析里最硬核、也最值得读懂的一段。问题背景是:固定输入,唯一随机性来自一个跟输入独立的均匀随机块排列。我们想知道——在最坏的输入分布下,所有 RadixSelectTopK 调用一共要处理多少元素?

m = ceil(N / B)L = k + B + B2H_m = Σ(1/i)(i 从 1 到 m,也就是第 m 个调和数)。文档证明了:在给定前 i 个已处理块集合 U_i 的条件下,第 i 个块加入候选的元素数量的期望 E[A_i | U_i] <= L / i;再由期望的线性性得到所有被追加元素总数 E[A] <= L · H_m。令 R 为循环内部调用 TopK 的次数,每次至少删掉 B2 个元素,所以 R · B2 <= A;而每次被追加的元素进一次输入、每次循环内调用保留的 k 个元素之后还会再被处理一次,于是总处理元素数 W = A + kR。最终得到:

math
E[W] <= (1 + k / B2) · L · H_m
     = (1 + k / B2) · L · (ln m + O(1))

这个式子对工程师意味着什么?它说的是:在随机块顺序下,RadixSelect 阶段「被反复处理的冗余元素总量」的期望被一个关于输入规模的对数项 ln(N/B) 所控制,而不是线性地跟随 N 增长。换句话说,即便输入有几十万、上百万个元素,真正需要进 radix-select 反复比较的量也只是 O((1 + k/B2)(k + B + B2) · log(N/B))

文档的性能综述进一步给出:当 B, B2 = Θ(k) 时,TopK 部分额外的计算量只有 O(k · log(N/k)),远小于输入规模 N。这正是「一遍扫描 + 阈值收敛」相比「全行排序」的本质优势:你不再为每一个元素都付出排序代价,而只为那一小撮真正可能进 top-k 的元素付出对数级的代价。经过针对硬件的细致优化后,它在 DSA 场景下相对 torch.topk 取得显著加速,也就不意外了。

五、为什么用"有效内存带宽"而不是 FLOP 来度量

README 把性能度量方式写得非常坦率:用 tests/test.pypython3 tests/test.py --perf-only)跑基准,指标是同一输入下相对 torch.topk 的加速比;而核心度量指标是「有效内存带宽」(effective memory bandwidth)。理由是:TopK 不做浮点运算,所以报一个 FLOP 速率在这里没有意义。

这句话值得每个做算子优化的人记住。很多同学习惯性地用 TFLOPS 衡量一切 kernel 快慢,但 TopK 的本质是「把数据从全局显存搬进来、比较、选出」,它吃的是显存带宽而不是算力。一个 TopK kernel 优不优,要看它把 HBM 带宽用到了多少(官方图表里把 Lightning Indexer 场景画在共享的 0 到 7 TB/s 坐标轴上),而不是看它跑了多少 FLOP。这也反向说明了 DeepSelect 的优化重心为什么落在「减少全局内存读取次数、连续块访问、把候选集塞进 shared memory、用位运算和 PTX 挤压常数因子」上——因为这些都在为带宽服务。

具体基准设置:Lightning Indexer 场景用 bfloat16、topk = 512,每个 batch size 一张子图;Sampling 场景用 float32、vocab_size = 129280topk = 512

六、使用场景与边界:它不做什么,比能做什么更重要

对一个极早期的算子库,最有价值的信息往往不是「它能做什么」,而是它的硬约束。DeepSelect 明确只支持两类场景,且都有刚性限制。

维度Lightning Indexer 场景Sampling 场景
输入 dtypetorch.bfloat16torch.float32
batch_size1 ~ +∞(大小 batch 都优化)1 ~ +∞
vocab_size1 ~ +∞(大小词表都优化)约 128K
topk必须 ≤ 4096(更大不支持)必须 ≤ 4096(更大不支持)

其余边界同样要记牢:

  • topk 硬上限 4096:大于 4096 的请求直接不在支持范围。这是算法与实现对特定 workload 的取舍,不是疏漏。
  • dtype 严格区分:Lightning Indexer 只吃 bfloat16,Sampling 只吃 float32,别混用。
  • 只对特定 shape 优化:仓库只针对上述两类 workload 的输入分布调优,并非对任意 shape 都快。
  • 行步长对齐要求:输入张量 x 的 row stride 必须对齐到 deep_select.get_stride_requirement()[0] 字节,且最后一维必须连续;未对齐的输入需要 padding。两个输出由调用方分配,步长对齐到 get_stride_requirement()[1] 字节(可能不连续);也可传 output_idx= 写入自己持有的缓冲,该缓冲也要满足同样的步长要求。
  • 变长行:用 end 设每行(不含)的上界,短于 topk 的行用 value_oob_fill_value / idx_oob_fill_value 填充。
  • NaN 处理始终开启:默认 abort_when_nan_found=True 会让 kernel 调用 trap() 并中止;长度 <= topk 的行不做 NaN 检查。

另外两条调参建议是 README 原文直接给的,能直接省性能:除非输出必须按 index 或 value 排序,否则关闭 sorted_index(开启任一种排序都付出性能);不需要取值时设 return_value=False,这会跳过 value 输出、更快(约 10%)。

顺带一提长上下文成本这个更宏观的话题——我们本批另一篇横评(点此阅读)系统性比较了不同长上下文方案的账单,而 DeepSelect 这类底层算子,正是把「长上下文稀疏化」从论文变成可落地推理的关键一环。如果你打算把 V4.1 Flash 接进自己的系统,本批的集成 SOP(点此阅读)也值得一并看。

七、冷思考:152 颗星与它的真实价值

最后泼一点冷水,帮读者建立正确预期。

第一,152 颗星意味着它处于极早期。发布当天就拿到这个星数,更多反映的是「DeepSeek 官方出品」的关注度,而不是社区已经大规模验证过它在生产里的稳定性。引用信息里作者署名为 Yi Qian、Shengyu Liu、Yichen Li(2026),项目还非常新,接口、边界、性能都可能在后续版本里变化。

第二,它对非 DeepSeek 系模型的直接用处有限。DeepSelect 是为 DSA(V3.2 / V4 / V4.1)和对应采样 workload 量身定制的,输入 dtype、topk 上限、shape 假设都绑定在 DeepSeek 的推理路径上。你拿一个完全不相关的任务、随便什么 dtype 和 shape 去套,既不在它的优化范围,也可能直接触发不支持的限制。把它当成「通用 TopK 加速库」来用,是误会了它的定位。

第三,它的价值更偏向生态层面而非通用层面。对 DeepSeek 系模型的用户和研究者,它把 DSA 的算子底座开放出来,意味着你可以审计、改写、针对自己的硬件再榨性能;对整个社区,它是一份「为特定 workload 手写 CUDA TopK」的高质量参考实现,里面的 PTX 与位运算技巧(下面第八节细说)本身就是教材。换句话说,它的意义不在于「让所有人的 TopK 都变快」,而在于「让 DeepSeek 的稀疏注意力真正跑得动、跑得省」,并顺手给算子优化者留下一份可学习的范本。

常见问题

Q1:DeepSelect 和普通的 torch.topk 有什么区别? A1:核心区别在两点。一是定位:torch.topk 是通用实现,要为各种 dtype、shape、topk 大小留余量;DeepSelect 只聚焦 DSA 的 Lightning Indexer 与采样两类 workload,为它们定制了「一次扫描 + 阈值收敛」的 RadixSelect 算法。二是性能:官方实测相对 torch.topk 取得 2 到 20 倍加速,且度量指标是有效内存带宽而非 FLOP。如果你的输入正好是它支持的两类场景,收益明显;否则它不支持或不保证更快。

Q2:为什么 DeepSelect 的 topk 必须小于等于 4096? A2:这是算法与实现对特定 workload 的明确取舍,不是遗漏。DSA 的 Lightning Indexer 与采样场景里,实际需要的 k(如基准中的 512)远小于 4096,DeepSelect 的候选缓冲区、压缩触发与 shared memory 布局都围绕「小 topk」设计。大于 4096 的请求不在支持范围,仓库不会保证正确性或性能。

Q3:DeepSelect 支持哪些输入数据类型? A3:只支持两类,且严格区分:Lightning Indexer 场景输入 dtype 为 torch.bfloat16,Sampling 场景输入 dtype 为 torch.float32。此外输入张量的 row stride 必须对齐到 deep_select.get_stride_requirement()[0] 字节、最后一维必须连续;未对齐的输入需要 padding。两个输出由调用分配,步长对齐到 get_stride_requirement()[1] 字节。

Q4:我能在自己的非 DeepSeek 模型里直接用 DeepSelect 吗? A4:可以调用,但要先确认你的 workload 落在它支持的两类场景内(bfloat16 的 Lightning Indexer,或 float32、词表约 128K 的 Sampling),且 topk ≤ 4096、输入满足步长与连续性要求。如果你的模型并不是 DeepSeek 系、输入分布与这两类场景不符,DeepSelect 不在优化范围,可能不更快甚至不被支持。它的第一性是为 DSA(V3.2 / V4 / V4.1)服务。

Q5:如何验证 DeepSelect 在我机器上的加速比? A5:用仓库自带基准:python3 tests/test.py --perf-only,它会报同一输入下相对 torch.topk 的加速比,指标是有效内存带宽。Lightning Indexer 场景基准用 bfloat16、topk=512(每个 batch size 一张子图),Sampling 场景用 float32、vocab_size=129280、topk=512。若要进一步省性能,记得按 README 建议关闭非必须的 sorted_index,并在不需要取值时设 return_value=False


参考来源

  • DeepSeek DeepSelect GitHub 仓库(152 星,CUDA,DeepSeek 官方组织,创建并 push 于 2026-09-10):https://github.com/deepseek-ai/DeepSelect
  • 官方 README(定位、支持场景、性能度量、安装与使用):https://github.com/deepseek-ai/DeepSelect/blob/main/README.md
  • 官方中文算法解析 docs/DeepSelect-deep-dive.zh.md(算法设计背景 / 算法 / 期望上界 / 性能综述 / 实现)
  • 官方英文算法解析 docs/DeepSelect-deep-dive.md
  • GitHub API 实测数据(2026-09-10):仓库 152 星、标签 v1.0.0、同日发布中英双语算法文档

本文由 AI 辅助生成,经人工审核编辑。最后更新:2026-09-10

常见问题

DeepSelect 和普通的 `torch.topk` 有什么区别?
核心区别在两点。一是定位:`torch.topk` 是通用实现,要为各种 dtype、shape、topk 大小留余量;DeepSelect 只聚焦 DSA 的 Lightning Indexer 与采样两类 workload,为它们定制了「一次扫描 + 阈值收敛」的 RadixSelect 算法。二是性能:官方实测相对 `torch.topk` 取得 2 到 20 倍加速,且度量指标是有效内存带宽而非 FLOP。如果你的输入正好是它支持的两类场景,收益明显;否则它不支持或不保证更快。
为什么 DeepSelect 的 topk 必须小于等于 4096?
这是算法与实现对特定 workload 的明确取舍,不是遗漏。DSA 的 Lightning Indexer 与采样场景里,实际需要的 k(如基准中的 512)远小于 4096,DeepSelect 的候选缓冲区、压缩触发与 shared memory 布局都围绕「小 topk」设计。大于 4096 的请求不在支持范围,仓库不会保证正确性或性能。
DeepSelect 支持哪些输入数据类型?
只支持两类,且严格区分:Lightning Indexer 场景输入 dtype 为 `torch.bfloat16`,Sampling 场景输入 dtype 为 `torch.float32`。此外输入张量的 row stride 必须对齐到 `deep_select.get_stride_requirement()[0]` 字节、最后一维必须连续;未对齐的输入需要 padding。两个输出由调用分配,步长对齐到 `get_stride_requirement()[1]` 字节。
我能在自己的非 DeepSeek 模型里直接用 DeepSelect 吗?
可以调用,但要先确认你的 workload 落在它支持的两类场景内(bfloat16 的 Lightning Indexer,或 float32、词表约 128K 的 Sampling),且 topk ≤ 4096、输入满足步长与连续性要求。如果你的模型并不是 DeepSeek 系、输入分布与这两类场景不符,DeepSelect 不在优化范围,可能不更快甚至不被支持。它的第一性是为 DSA(V3.2 / V4 / V4.1)服务。
如何验证 DeepSelect 在我机器上的加速比?
用仓库自带基准:`python3 tests/test.py --perf-only`,它会报同一输入下相对 `torch.topk` 的加速比,指标是有效内存带宽。Lightning Indexer 场景基准用 bfloat16、topk=512(每个 batch size 一张子图),Sampling 场景用 float32、vocab_size=129280、topk=512。若要进一步省性能,记得按 README 建议关闭非必须的 `sorted_index`,并在不需要取值时设 `return_value=False`。

相关文章

开源项目

DeepSeek-Reasonix:DeepSeek 原生终端 coding agent(GitHub 星 2.86 万)

esengine/DeepSeek-Reasonix(28,575 星、1,836 fork、Go、MIT、2026-04-21 创建、今日 push)是社区构建的 DeepSeek 原生终端 coding agent,非 DeepSeek 官方产品。围绕 DeepSeek prefix cache 调优--缓存命中输入 0.02 元 vs 未命中 1 元,差 50 倍。单一 Go 静态二进制,config/plugin 驱动(reasonix.toml),支持双模型 executor+planner、MCP 插件、跨 6 平台编译。附四条安装路径与同类对比。

2026年8月2日8 分钟阅读
开源项目

LLaDA-Image:蚂蚁全开源的 6B 统一生图编辑模型

蚂蚁 InclusionAI 开源 6B 统一图像生成与编辑模型 LLaDA-Image(GitHub 2026-09-09 快照 208 星 / Python / 创建 2026-08-31)。单一权重同时做文生图与指令编辑,backbone 与 DiT 都是扩散模型的统一框架,图像-only 预训练建视觉先验,Turbo 版用 Twin-DMD 蒸馏把 50 步压到 4 步;Qwen-Image-Bench 英文 53.53、中文 53.38 双料 SOTA;HF 与 ModelScope 提供 Base/Turbo 各含 FP8 四档权重,9-07 起有社区 ComfyUI。最重要避坑:仓库 license 字段为 null、无 LICENSE 文件,商用前须向官方确认,不可臆测为 Apache/MIT。

2026年9月9日10 分钟阅读
开源项目

OpenMAIC周增八千星登顶,把文档变多智能体课堂

THU-MAIC/OpenMAIC 以周增 8,095 星登顶 GitHub 周榜(2026-09-08 快照 33,053 星 / 5,369 fork / TypeScript / MIT)。它把任意主题或文档变成多智能体互动课堂:AI 老师与 AI 同学实时讲课、讨论、白板板书、TTS 朗读,生成幻灯片、测验、交互式模拟与 PBL 项目制学习,可导出 .pptx 与交互 HTML。v1.0.0(2026-08-27)新增 Agent workbench 聊天式构建、持久化会话、20 个内置技能;技术栈 Next.js 16 / React 19 / LangGraph 1.1;v0.3.0 起由 AGPL-3.0 转 MIT,并内置 SKILL.md 标准技能包,可从 OpenClaw、Codex、WorkBuddy 等工作台直接生成课堂。

2026年9月8日10 分钟阅读