向量库和 GPU 显存怎么分:几十万条向量该放内存还是放显存
在只判断“同一批向量能否全部驻留显存”的边界下,这首先是一个纯容量题,不需要效果评测。只需核对维度、条数、存储格式,以及裸向量占用是否已经碰到显存上限。
本文所有“占用”读数都是下限估算:只计算能够由公式准确得到的向量本体,真实工程占用只会更高。读数分为三类:①公式能够纯算术确定;②公开算法结构能够推出附加存储项;③取决于实现、运行时和参数,本站不给猜测倍数。
先把裸向量的字节算出来
① 公式确定:fp32 裸向量
设向量维度为 d,条数为 n。Faiss 官方 wiki 列出的 IndexFlatL2 存储基数为 4*d,也就是每条 fp32 向量需要 4 × d 字节。
因此,只计算向量本体时:
L_fp32(n,d) = n × 4 × d 字节
这个结果精确到字节,但有明确边界:它不包含邻接结构、索引元数据、分配器保留空间、运行时工作区或临时缓冲。
以维度 1536 为例:
4 × 1536 = 6144 字节 ≈ 6 KB
对应的裸向量占用下限为:
L_fp32(n,1536) = n × 6144 字节
1536 只是本次现场测算的输入,不是所有向量库的固定维度。维度变化后,应重新使用 4 × d 计算。
② 公开结构可推出:附加项存在,但未必算得出字节
公开索引结构可以告诉我们还存在哪些存储项。例如,Faiss 官方 wiki 将 IndexHNSWFlat 的存储规模写成:
4*d + x*M*2*4
其中,4*d 对应向量本体,后面的项由 x 和 M 决定。没有公开的 x、M 输入,就不能把附加项换成准确字节数。
这属于公开算法结构能够推出的部分,但不能继续乘一个猜测倍数。邻接记录怎样分配、参数取什么值,仍然属于③。
③ 本站算不准:完整索引实际占用
完整索引还可能涉及实现专有对象、内存分配保留、运行时缓存和工作空间。现有输入没有这些实现参数,因此本站不提供“fp32 裸向量再乘多少”的答案。
所以,后文出现的所有容量除法都只是裸向量门槛,不是完整索引的保证容量。
一张 H200 有多少显存:141 GB 与 4.8 TB/s
NVIDIA H200 官方规格页给出的 GPU Memory 是 141 GB,GPU Memory Bandwidth 是 4.8 TB/s。这两个数字都是固定规格输入,不是容量估算结果。
本文按资料给出的十进制口径做容量除法:
M_H200 = 141 × 10^9 字节
4.8 TB/s 是带宽规格,不是显存容量。它不能拿来抵消容量不足,也不能直接当作主机内存到显存的实际搬运速度。有效搬运时间还取决于数据路径、页锁定状态和具体工作负载,本站不据此计算延迟。
还要注意,141 GB 是官方总规格,不是某次运行时可以自由使用的显存。CUDA 官方文档中的 cudaMemGetInfo 可以分别取得设备端空闲显存和总显存;没有实际运行结果,就不能把 141 GB 当成 M_free。
因此,由 141 GB 得到的条数只能用于名义容量的量级判断。真实可驻留条数还会更少。
显存不是普通内存:页锁定内存这条硬前提
显存与主机内存不是同一种资源。141 GB 描述的是设备端显存;页锁定内存仍属于主机内存,只是其页面被注册为设备可访问,并可由驱动加速相关内存操作。
CUDA 官方接口给出了明确路径:
cudaHostAlloc分配页锁定并且设备可访问的主机内存。cudaMallocHost分配页锁定主机内存。cudaHostRegister将已有主机内存范围注册给 CUDA 使用。cudaMallocManaged使用自动管理的统一内存接口。
硬前提不是“普通 malloc 得到的内存可以无条件视为设备内存”,而是:如果主机侧缓冲区需要被设备直接访问,或需要走高带宽直接访问路径,就应先通过页锁定分配或注册接口满足 CUDA 的条件。
普通可分页内存仍可能参与复制流程,但其设备直接访问能力与页锁定内存不同。CUDA 官方文档明确指出,页锁定内存可由设备直接读写,带宽明显高于可分页内存。因此,不能把“能够复制”直接等同于“已经高效驻留”。
页锁定也不是没有代价。CUDA 官方文档明确提醒,过量分配页锁定内存会减少系统可用于分页的内存,并可能降低系统性能。把整批数据都注册为页锁定内存之前,必须把主机侧的内存预算一起算进去。
本站没有系统总内存、当前可分页内存和计划锁定字节数,因此无法计算页锁定造成的系统侧准确占用,也不会给出经验百分比。
“整块驻留”约束的是同一计算批次
本文所说的“整块驻留”,是指交给同一设备端计算批次处理的向量,在执行时需要同时占用设备显存。它不表示整个向量数据库必须永久驻留,也不要求所有数据在物理地址上构成一个连续内存块。
设同一批次有 n 条向量,每条裸向量占 s(d) 字节,则必要容量条件是:
n × s(d) < M_free
这里必须使用运行时可用显存 M_free,不能直接拿 141 GB 代替。
如果:
n × s(d) ≥ 141 × 10^9
那么即使完全忽略索引、运行时和临时缓冲,裸向量也已经达到或超过 H200 的名义总显存,整批驻留可以直接判定为不成立。
反过来,如果裸向量低于这个门槛,也只能说“容量上尚未被排除”。这属于①的纯算术结论;实际是否装得下仍是③,因为 M_free 和额外占用都不知道。
现场算:一张卡到底装得下多少条向量
现在把维度、格式都固定下来:
- 维度:
1536 - 格式:fp32
- 每条裸向量:
4 × 1536 = 6144字节 - H200 名义显存:
141 × 10^9字节
裸向量容量商为:
q_fp32 = 141 × 10^9 ÷ 6144 ≈ 2.4 × 10^7
因此,在“1536 维、fp32、整批数据连续占用、整张名义显存全部用于向量本体”的假设下,量级是两千多万条。
这不是一张卡实际可保证装载的固定条数。它表示裸向量下限达到 141 GB 时的容量门槛。实际运行时还有空闲显存损失和实现开销,真实可驻留规模只会更小。
这个例子也说明,标题中的量级不能脱离维度和存储格式直接成立。判断顺序应当是:
n × 每条裸向量字节数
先得到占用下限,再与设备端可用显存比较。占用下限已经超卡时,可以直接判定整批驻留不成立;占用下限没有超卡时,才进入③继续核查运行时条件。
换精度之后呢:int8 与二值把条数放大多少倍
这里只比较存储容量,不评价检索质量。
int8
Faiss 官方 wiki 给出的 SQ8 存储基数是 d 字节。对 1536 维向量:
s_int8 = 1536 字节
与 fp32 裸向量相比:
6144 ÷ 1536 = 4
所以在其他条件完全相同、只比较裸向量时,int8 的容量商是 fp32 的 4 倍:
q_int8 = 141 × 10^9 ÷ 1536 = 4 × q_fp32
沿用前面的量级标尺,就是从两千多万条放大到九千多万条。这里仍然只是裸容量门槛,不是完整索引的保证容量。
二值
公开索引表的二值存储使用 ceil(nbits/8) 字节。对 1536 维、每个分量采用一个二值存储位时:
s_binary = ceil(1536/8) = 192 字节
与 fp32 裸向量相比:
6144 ÷ 192 = 32
因此,只比较裸向量时:
q_binary = 141 × 10^9 ÷ 192 = 32 × q_fp32
容量商放大为 fp32 的 32 倍,量级进入七亿多条。
4 倍 和 32 倍 是①类裸向量结论。若还包含索引结构、实现元数据或共享对象,完整占用的放大关系会受实现影响,仍属于③,不能把裸向量倍数直接当成完整索引倍数。
什么时候这条路一开始就不成立:条数或维度已经超卡
容量判断可以从两个方向进行。
单条向量检查:
s(d) ≥ 141 × 10^9
满足这个条件时,即使只有一条 fp32 裸向量,也已经无法在这张 H200 上完整驻留。
整批向量检查:
n × s(d) ≥ 141 × 10^9
满足这个条件时,同一批向量不可能全部同时驻留。此时容量上只剩几种算术可能:缩小同批驻留规模、降低每维存储字节数,或者把数据分布到多个设备显存空间。哪一种可行,取决于实现与工作方式,本文不做选型。
如果问题来自分布式维度,单个向量可以按分量拆到多个设备,但此时向量已经不再完整驻留在单个设备上。现有输入无法判断具体实现是否支持这种访问方式,因此属于③。
页锁定内存也不能扩大 H200 的 141 GB 设备容量,4.8 TB/s 同样不能补足容量缺口。它们解决或描述的是数据访问与搬运条件,不是显存字节本身。
对于带有公开附加结构的索引,判断还必须把附加项纳入。例如 IndexHNSWFlat 的 4*d + x*M*2*4 已经说明,完整规模不只是 4*d。没有参数和实现输入时,最严谨的结论仍是“裸向量门槛通过或失败,完整容量算不准”。
把容量结论带回使用边界
本站最终只给出两类明确判断:裸向量字节能够精确计算;裸向量门槛与 H200 名义显存能够直接比较。实际空闲显存、索引参数、页锁定规模和运行时开销则单独标记为算不准,不提供猜测倍数。
继续代入自己的维度、条数、格式和索引类型时,可以先看使用边界与测算规则;需要理解本站为何只做容量读数时,再看关于本站。
本文只回答容量装不装得下,不回答哪个更好用。
本文为容量测算方法说明,不构成选型建议。