FlashLib:聚类、检索等经典算法在H200上狂飙26倍


FlashLib重写经典ML算子,通过算法重塑、硬件适配、精度路由和成本预估,在GPU上实现数十倍加速,赋能智能体系统。

FlashLib让老掉牙的机器学习算法在GPU上飞起来

以前那些聚类啊、找邻居啊、降维啊这些老算法,跑起来慢得要命,尤其数据一多,能等到怀疑人生。

现在FlashLib来了,它把这些经典算法整个重写了一遍,让它们在最新的H200显卡上跑得飞快。核心就一句话:把算法改成显卡喜欢的样子,再根据你要多准的结果,自动选最快的实现方式。结果就是,同样算KMeans聚类,FlashLib比老牌GPU库cuML最快能快26倍,KNN快19倍,PCA快47倍,TruncatedSVD甚至快了208倍。

这还不算完,它还能提前告诉你这段代码跑起来要多久、用多少显存,精确到几微秒,不用真的上显卡去试。

老算法之所以慢是因为它们的设计跟不上时代了

早些年,大家做机器学习,数据量没那么大,聚类啊、降维啊这些活儿都是离线跑的,花个几分钟甚至几小时都没人管。那时候的算法库,比如scikit-learn,根本没想着要去用显卡加速。后来有了GPU版的库,比如cuML,但它们的核心设计思路还是老一套。

打个比方,这就像你想在高速公路上开拖拉机,虽然路好了,但车本身不行。这些老库的算法实现,动不动就在显存里生成一个巨大的中间表格,比如KMeans,它会把每个点和每个聚类中心的距离全算出来,存成一个N行K列的超级大表(N是点数,K是聚类中心数)。当你有100万个点,要分1024类的时候,这个表就有10亿个数字,光存就得4GB显存,读写它就花掉大把时间。

更搞笑的是,我们其实只需要知道每个点离哪个中心最近,这个巨大的距离表根本没必要全部算出来存着。

第一步我们把算法改成显卡喜欢的样子

显卡这东西,特别擅长做同样的事情重复无数次。但它最怕两件事:一是要频繁地把中间结果写回显存(那玩意儿叫HBM,读写速度虽然快,但还是远不如显卡里的寄存器快),二是不同线程要抢着写同一个地方(这叫原子操作冲突)。所以FlashLib的第一板斧,就是把算法重写,让它们不在显存里生成巨大的临时表格,而是把所有能合并的计算都合并到一起。

就拿KMeans来说,传统做法是先算出一个N×K的距离矩阵存着,然后每行找最小值。

FlashLib的做法是:一个点一个点地处理,每读一个点进来,立马和所有聚类中心算距离,只记住当前最小的那个距离和对应的中心编号,然后这个点就可以扔掉了。这样整个计算过程中,除了原始数据和最后的聚类结果,几乎不占用额外显存。这就像你去超市买东西,传统做法是你把所有商品价格全记下来,然后回去慢慢算哪个最便宜。

FlashLib的做法是你边逛边比价,脑子里只记住当前看到的最低价,逛完直接就买最便宜的那个。这个改进在KNN(找最近邻)里也类似,我们计算两个向量距离的时候,把公式展开,消掉那些对最终排序没影响的项,减少计算量。

第二步针对不同硬件和情况做多种版本的核函数

光把算法改好还不够,因为显卡也在不断进化。老一点的A100和最新的H200,它们里面的结构不一样,有的运算单元更强,有的显存带宽更大。FlashLib的第二个思路是,为同一个操作准备多个版本的内核(kernel),然后根据你的数据和硬件,自动选最合适的那个。

KNN就是个好例子。如果你要查的是一大堆点,我们就用类似FlashAttention那种模式,把计算分成很多小块,尽量用满显卡里专门做矩阵乘法的Tensor Core。但如果你只查一个点,却要从1亿个点里找邻居,那再用上面那个方法,显卡很多计算单元就闲着没事干了。这时候我们就换成另一种模式,把那一亿个点分成好多段,每个计算单元负责一段,最后再合并结果。

这种灵活切换,让哪怕只查一个点这种极端情况,也能把H200显卡的显存带宽用到85%以上。这就像送快递,货多的时候用大卡车装满跑高速最划算;货少的时候,就换成小面包车走街串巷,反而更快。

第三步让你自己选要多准的结果来换取更快的速度

很多时候,我们其实不需要算得那么精确。比如做图像检索,你把一张猫的图片转成特征向量去找相似的猫,哪怕计算结果稍微有点误差,比如0.0001,最后找到的很可能还是那只猫。但传统的库,默认都给你算得死死的,用单精度浮点数(FP32),甚至双精度(FP64),慢得要死。FlashLib让你可以用一个叫tol的参数,告诉它“我允许的误差大概这么大”。然后它会自动帮你挑最快的实现方式。

比如你要算两个大矩阵的乘法(GEMM)。你如果要求一点都不差(tol=None),它就老老实实用最精确的FP32算。你说误差1e-3就行,它立马换成BF16(一种不那么精确但快很多的数据类型),速度翻倍。你说1e-7就行,它用三个BF16拼起来算,又快又相对准。你说要1e-12,它甚至能用INT8整数运算配合一套叫Ozaki-II的数学技巧,达到接近双精度的准度,但速度比双精度快好几倍。

做PCA(主成分分析)也一样,你要求精确,它就老老实实算特征向量;允许一点误差,它就换一种叫Halko的随机近似算法,能快30倍。这就是用一丢丢精度换起飞的速度,划算。

第四步在动手前就能告诉你花多少钱

现在很多程序都是大模型(LLM)这个智能体(agent)写的。它会自己上网找代码,拼凑成一套流程。但如果它选了一个巨慢的聚类算法,整个系统就崩了。它又不可能每次都真的跑一遍来测试速度,太费时间了。FlashLib提供了一个神级功能:flashlib.info.estimate(...)。你只管告诉它你要做什么操作(比如PCA)、数据多大(比如100万行512列)、用什么显卡(比如H200),它在你电脑的CPU上,不用真的调GPU,几微秒内就能返回一个预估结果:大概跑13.18毫秒,主要瓶颈是计算,会用到42万亿次浮点运算每秒(TFLOPS),达到83%的峰值性能。

它还能把内部子步骤的耗时都拆给你看,比如里面矩阵乘法占10.49毫秒,特征分解只占0.12毫秒。这样大模型智能体就可以在真正干活前,先把整个流程的成本算清楚,决定要不要这么干,甚至比较不同方案的优劣。

看看实际效果到底有多猛

我们拿FlashLib和目前最主流的GPU机器学习库cuML,在同一种H200显卡上做了个公正的对比。为了保证公平,我们禁止FlashLib使用上面说的任何精度换速度或者算法近似的技巧,纯粹比谁的内核写得好。结果测了13种基本操作,194种不同的数据形状和参数组合,在193种情况下FlashLib都比cuML快。其中126种情况下快了5倍以上,11种情况下快了50倍以上。注意,这只是纯工程优化的胜利,还没用上那些作弊级的大招。

然后我们再看一个更贴近现实的场景。我们给一个写代码的AI助手(Claude Opus 4.7)派了个任务:用100万token的预算(也就是它能思考和搜索的总“脑力”),在SIFT-1M数据集上,写一个向量检索的后端系统,要求在保证至少99.9%的召回率(就是不能漏掉正确结果)前提下,让每秒查询数(QPS)尽量高。我们给AI两次机会,一次禁止它用FlashLib,一次允许它用。

结果差距巨大。在做离线批量查询(一次查1万个向量)这种核心任务时,没有FlashLib的那个AI,折腾来折腾去,最后卡在了每秒5万次查询,预算用完也没辙了。而能用FlashLib的那个AI,直接拿我们的flash_knn函数,轻松把性能干到了每秒31万次查询,提了6倍多,而且预算还没花完。在流式查询(数据一小批一小批来)这种场景,性能也提高了5倍多。只有在一次只查一个向量的极端情况,因为启动程序本身的固定开销太大,两者才差不多。

这个实验很清楚地说明,FlashLib不仅是快,它还能让写代码的AI更容易写出高性能的程序。

总结一下

FlashLib就是一个专门给经典机器学习算子设计的GPU加速库。它通过四点创新:
1)把算法重写成显卡友好的形式;
2)针对不同场景做多个内核版本;
3)允许用户用精度换速度;
4)提供精准的性能预估接口,彻底把KMeans、KNN、PCA、SVD、t-SNE、HDBSCAN这些基础算子从慢吞吞的离线工具,变成了能在Agentic AI(基于大模型的智能体系统)里实时调用的闪电快枪手。

而且代码完全开源,用Triton和CuteDSL写成,没有黑盒子,你和大模型智能体都能看明白、能改、能用。