GPU Gems 3 · Chapter 35. Fast Virus Signature Matching on the GPU

从网络数据的重叠字节窗口出发,解释 GPU 如何用 2-byte key 和 4-byte tag 做病毒签名候选过滤,再由 CPU 验证并通过多 buffer 管线隐藏延迟。

学习目标

  • 能解释为什么病毒检测要把“快速筛出候选”和“验证完整签名”拆成 GPU 与 CPU 两段
  • 能修改 GPU Virus Filter Lab 的签名表规模、输入 buffer、tag 命中率、验证策略和 buffer 管线,观察候选回读如何改变吞吐
  • 能回答:为什么一个命中 tag 不等于发现病毒,以及当签名跨 buffer 边界时怎样避免漏检

先问:为什么要先做一个很快的筛子

网络设备面对的不是一份整齐的文件,而是一条持续流入、大小不一、可能跨越多个对象的字节流。每一段数据都可能包含恶意模式;如果每次都让最慢的完整检查从头读到尾,吞吐很快就会被拖垮。

本章把工作拆成两层:先用大量轻量线程找出“看起来像”的位置,再把少量候选交给更擅长复杂判断的处理器。这样没有候选时可以快速通过,有候选时也仍然保留完整检查的准确性。

1. 把完整签名匹配拆成候选过滤和最终验证

原始库以固定字符签名为第一步:GPU 负责规则短、访存规律的候选筛选,CPU 负责完整签名验证和对象归属。正则表达式、解压、文件类型判断和最终报告并不自动变成 GPU 工作;它们仍属于应用或 CPU 侧的可变逻辑。

GPU filter first, CPU verification secondinput bufferpacket / fileobject tablefill before scanGPU filter2-byte key lookupshort, regular threadspossible matches onlyCPU verifyfull signatureexact / regex workreport objectmap offset → filethe GPU is a high-speed filter, not the final antivirus decision

GPU 与网络处理器有相似之处:都需要快速内存、用很多线程隐藏 DRAM 延迟,并持续搬运大批量数据。但相似不意味着所有阶段都适合并行;“候选过滤”才是本章刻意挑出的规则核心。

2. 选择并行方向:一个包内,还是多个包之间

intrapacket 让一个长对象内部的相邻窗口并行;interpacket 则让多个对象同时前进。实际库把数据先填满 buffer,再由 GPU 忽略对象边界地扫描连续字节,最后由 CPU 使用对象描述把候选归回文件。这个设计避免 GPU 在线程中维护复杂的文件状态。

choose where the parallel bytes come fromintrapacket scanningone packet, many byte windowsa1b72f809104T0T1T2T3high local reuseboundary and overlap handling matterinterpacket scanningmany packets, one window per threadpacket Apacket Bpacket Cindependent objectsless shared state, more concurrencyboth layouts fit the GPU; the library can choose based on packet boundaries and buffer occupancy

两种布局的共同要求是:线程必须能独立读固定数量的字节,输出也必须能写到确定位置。于是下一步不是把完整签名复制给每个线程,而是为每个窗口准备一个很小的索引和比较值。

3. 用 2-byte key 把签名数据库压成一次查表

对每个固定字符签名,数据库取一对连续字节作为 key,并把它前面的四个字节存成 tag。GPU 线程读取输入中的一个 2-byte window,用这个 key 索引表,再把表里的 tag 与输入窗口之前的四个字节比较。两个比较都匹配时,只说明这里值得 CPU 进一步检查。

可以把线程的主循环压缩成下面的伪代码;关键不是语言,而是每个线程读取固定窗口、做一次索引和一次短比较:

for each adjacent pair at offset i:
  key = input[i : i + 2]
  tag = table[key]
  if input[i - 4 : i] matches tag:
    output[2 * i] = signatureId
turn a long signature into a tiny table lookupsignature bytes6d616c7761722-byte key: 6c 77preceding four bytestag: 6d 61 6c 7764,000-entry tableindex = 6c77stored tag6d 61 6c 77at most one signaturein the original tablecompare6d61tag match?candidate offsetthe key narrows the search; the tag keeps the GPU test cheap but deliberately conservative

这个表故意把“快”放在第一位:每个 entry 原始设计至多对应一个签名,避免 GPU 为每个窗口追逐复杂链表。代价是签名集合、key 唯一性和内存容量都成为明确的可扩展性边界。

4. 重叠窗口与结果缓冲区决定正确性成本

线程 A 读取 [i, i + 1],线程 B 读取 [i + 1, i + 2],这种重叠让每个连续 2-byte pair 都有机会成为 key。若签名的关键字节跨越输入 buffer 末尾,应用必须复制足够的前置数据到下一个 buffer;复制量至少要覆盖最长签名所需的上下文。

overlapping windows keep signatures from hiding at boundariesinput bytesoffset0123456payloadthread i: [1,2]thread i+1: [2,3]every adjacent 2-byte paircan start a candidate signaturebuffer boundaryend of buffer Acopy prefixbuffer B startsup to longest signatureobject descriptions stay with the CPU; the GPU only sees a continuous byte buffer

命中时 GPU 不写入整条签名,而是在输出 buffer 的相对偏移 2i 写入两字节 identifier。没有命中的位置保持空值,CPU 之后扫描这些稀疏结果,再根据对象描述把 offset 转成文件或数据包中的位置。

sparse results are fast until the CPU must inspect them allinput offset i00010203candidate at i = 2write identifiernot full signature byteswrite bufferoffset = 2isignature idzero means no candidate2× input buffer sizeCPUscan countverifymap objectthreshold maystop verificationzero matches keep the readback cheap; positive density moves work from GPU to CPU

动手走一遍:一个 buffer 如何完成过滤并回到对象结果

分步1 / 4

先填满输入 buffer

CPU 先完成文件类型判断、解压等可变工作,把一个或多个对象拼入连续 buffer,并保留对象描述与跨边界所需的前置字节。

GPU filter first, CPU verification secondinput bufferpacket / fileobject tablefill before scanGPU filter2-byte key lookupshort, regular threadspossible matches onlyCPU verifyfull signatureexact / regex workreport objectmap offset → filethe GPU is a high-speed filter, not the final antivirus decision

5. 用多 buffer 把两个处理器变成一条流水线

如果 CPU 填一块 buffer、等待 GPU、再读回结果,GPU 工作时 CPU 可能闲着,CPU 验证时 GPU 也可能闲着。轮转多个 buffer 后,CPU 可以填充下一块,GPU 扫描当前块,CPU 同时验证上一块;只有结果 buffer 的回读在原始实现中仍然是串行点。

rotate buffers to overlap variable CPU work with regular GPU work1 · fillB1B2 nextCPU producer2 · filterkey → tagGPU regular pathwrite sparse ids3 · verifyfull signatureCPU cache workonly candidatesmap object result4B0 → B1overlaplatencyonly positive result readback is serialized in the original implementation

第 1 / 4 步 · CPU 继续填充下一个数据 buffer,同时把当前 buffer 提交给 GPU

逐步观察 CPU 和 GPU 如何通过多 buffer 轮转隐藏等待。

轮流使用多块输入/输出 buffer,让 CPU 填充、GPU 过滤和 CPU 验证在不同 buffer 上重叠执行,可以隐藏传输与执行延迟。

这种管线的收益依赖输入分布。没有 tag 命中时,GPU 可以连续处理大块数据,原文测量中最高获得 27 倍于 CPU 参考路径的加速;命中率升高后,结果回读与 CPU 验证会吞掉收益。这里的数字属于特定旧硬件和测试条件,不应当当成今天所有 GPU 的固定承诺。

6. 用 GPU Virus Filter Lab 观察吞吐如何被候选拖慢

GPU Gems 3 · Chapter 35

GPU Virus Filter Lab

可交互

调节签名表规模、输入 buffer、命中率、CPU 验证策略和 buffer 管线,观察候选数量与回读成本如何移动。

bytes → key/tag filter → candidate ids → CPU decisioninput64K tablekey → tagoutput ids481,964 candidatesCPU8,388,607 overlapping windows · 17× reference path高:候选结果需要回读 · 落在单 key 表容量内CPU 继续验证候选 · 134,950 CPU verification checksthe filter is conservative: candidate does not mean infectedpositive density moves cost toward result readback and verification
overlapping windows8,388,607
candidate ids481,964
CPU checks134,950
relative filter path17×

先保持 30,000 signatures8 MB buffer 和 rotating buffers,把命中率从 0% 拉到 50%;再切换 full verificationblock thresholdobject threshold。你应该看到:过滤阶段仍然规则,但 candidate ids、结果搬运和 CPU checks 会随着命中密度上升。

当数据库切到 120,000 时,Lab 会显式显示 table overflow。这个状态不是“GPU 变慢一点”这么简单,而是告诉你原始单 key 表的表示能力已被超过:必须引入多候选结构、分层查表或让 CPU 承担更多消歧工作。

小结

  • GPU 适合做规则、短路径的候选过滤,CPU 仍负责完整签名验证和对象报告。
  • intrapacket 与 interpacket 是两种数据并行布局,连续 buffer 让 GPU 可以暂时忽略对象边界。
  • 2-byte key 索引约 64,000-entry 表,tag comparison 用四个前置字节快速排除多数窗口。
  • 重叠窗口和跨 buffer 前缀复制防止签名在边界处漏检;命中结果写入 2i 的稀疏输出位置。
  • 多 buffer 轮转能隐藏延迟,但 false positive 密集时回读和 CPU 验证仍会成为瓶颈。

练习

练习

问题 1|修改 Demo 代码。 在 Lab 中保持 30,000 signatures,分别使用 0%、10%、50% tag match rate,比较 serial 与 rotating buffers 的 relative filter path、candidate ids 和 CPU checks。说明为什么 GPU 速度不等于端到端速度。

问题 2|诊断边界漏检。 一个签名在 buffer A 的最后三个字节和 buffer B 的前几个字节拼成完整匹配,但系统只在 B 开头重新开始扫描。请指出数据准备和 offset 映射需要怎样修改。

问题 3|场景选型。 一个几乎没有命中的高速流量、一个命中率高但允许提前标记 suspect 的网关、一个必须精确处理正则签名的离线扫描器,分别选择管线和验证策略。

名词解释

本章出现的专业名词,用大白话再讲一遍。

signature matching

把输入字节与已知模式逐段比较,并最终确认是否真的匹配的过程。

intrapacket scanning

在一个文件或数据包内部,把相邻字节窗口分给多个线程并行检查。

interpacket scanning

同时处理多个文件或数据包,让不同对象占据不同的并行工作单元。

2-byte key

用来索引 GPU 签名表的连续两个字节,原始结构约有 64,000 个位置。

tag filter

跟随 key 存储的四个前置字节比较,用便宜的短检查筛出候选。

false positive

过滤器报告了候选,但 CPU 验证完整签名后发现并没有真正匹配。

资料与写作方式声明

本章以GPU Gems 3 · Chapter 35. Fast Virus Signature Matching on the GPU权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…