5 高带宽性能指南

Xillybus IP 核的用户经常进行数据带宽测试,以确保达到标称的数据传输速率。要实现这些目标,需要避免可能严重拖慢数据流的瓶 颈。

本节是一组基于最常见错误的指南。遵循这些指南应能获得等于或略高于公布的带宽测量结果。

当然,在基于 Xillybus 的项目实现中遵循这些指南非常重要,以便项目能够充分利用 IP 核的全部能力。

问题往往在于主机处理数据的速度不够快: 错误地测量数据速率是抱怨无法达到公布数字的最常见原因。推荐 的方法是使用 Linux 的“dd”命令,如下面第 5.3 节所示。

本节的信息对于“入门”指南来说相对高级。讨论中还引用到其他文档中解释的高级主题。尽管如此,本指南仍提供这些指 南,因为许多用户在熟悉 IP 核的早期阶段就会进行性能测试。

5.1 不要进行回环

在 demo bundle(FPGA 内部)中,两对流之间存在回环。这使得“Hello, world”测试成为可能(见第 3 节),但这不利于测试性 能。

问题在于 Xillybus IP 核以数据突发传输的方式非常迅速地填满 FPGA 内部的 FIFO。由于此 FIFO 变满(full),数据流会暂 时停止。

回环是通过这个 FIFO 实现的,因此该 FIFO 的两端都连接到 IP 核。作为对 FIFO 中存在数据的响应,IP 核从 FIFO 中取出 数据并将其发送回主机。这也发生得非常快,因此 FIFO 变空(empty)。数据流再次暂时停止。

由于数据流中的这些暂时停顿,测得的数据传输速率会低于预期。这是因为 FIFO 太浅,并且 IP 核同时负责填充和清空 FIFO。

在实际场景中并不存在回环。相反,在 FIFO 的另一侧有应用逻辑。考虑达到最大数据传输速率的使用场景: 在此场景中,应用逻辑消耗 FIFO 中数据的速度与 IP 核填充 FIFO 的速度一样快。因此 FIFO 永远不会变满。

同样对于相反方向: 应用逻辑填充 FIFO 的速度与 IP 核消耗数据的速度一样快。因此 FIFO 永远不会变 空。

从功能角度来看,FIFO 偶尔变满或变空并没有问题。这只会导致数据流暂时停顿。一切都能正确工作,只是无法达到最大 速度。

为进行性能测试,可以轻松修改 demo bundle: 例如,为了测试 /dev/xillybus_read_32,将 user_r_read_32_empty 与 FPGA 内部的 FIFO 断开,并将此信号连接到常量零。结果,IP 核会认为 FIFO 永远不会变空,因此数据传输会以 最大速度进行。

这意味着 IP 核偶尔会从一个空的 FIFO 中读取数据。结果,到达主机的数据并不总是有效(由于下溢)。但对于速度测试, 这并不重要。如果数据内容很重要,一种可能的解决方案是让应用逻辑尽可能快地填充 FIFO(例如,使用计数器的输出)。

同样,测试 /dev/xillybus_write_32: 将 user_w_write_32_full 与 FIFO 断开,并将其连接到常量零。IP 核会认 为 FIFO 永远不会变满,因此数据传输以最大速度进行。发送到 FIFO 的数据将因溢出而部分丢失。

请注意,断开回环允许分别测试每个方向。然而,这也是同时测试两个方向的正确方法。

5.2 不要牵涉磁盘或其他存储

磁盘、固态硬盘及其他类型的计算机存储通常是带宽预期无法达到的原因。高估存储介质的速度是一个常见错误。

操作系统的缓存机制增加了混乱: 当数据写入磁盘时,物理存储介质并不总是被涉及。相反,数据被写入 RAM。只有稍后这些数据才会被写入磁盘本身。也可能是从磁盘读取数据不涉及物理介质。当相同的数据最近已被读取过时就会发生这种情 况。

在现代化计算机上,缓存可能非常大。因此,在磁盘的真实速度限制变得可见之前,可以流动数 GB 的数据。这常常使用户 认为 Xillybus 的数据传输有问题: 没有其他解释可以说明数据传输速率的这种突然变化。

对于固态硬盘(flash),还存在另一个混乱来源,尤其是在长时间连续写入操作中: 在闪存驱动器的底层实现 中,必须擦除未使用的内存段(块),以准备写入闪存。这是因为数据只能写入已擦除的块。

初始时,闪存驱动器通常有许多已擦除的块。这使得写入操作很快: 有大量空间可以写入数据。然而,当没有 更多已擦除的块时,闪存驱动器被迫擦除块,并可能执行数据碎片整理。这可能导致明显的减速,且没有明显解释。

由于这些原因,测试 Xillybus 带宽时绝不应涉及任何存储介质。即使在短时测试中存储介质看起来足够快,这也可能产生误 导。

通过测量将数据从 Xillybus 设备文件复制到磁盘上的大文件所需的时间来估算性能是一个常见错误。尽管此操作在功能上是 正确的,但用这种方式测量性能可能完全错误。

如果存储设备旨在作为应用的一部分(例如数据采集),建议彻底测试该存储介质: 应对存储介质进行广泛、 长期的测试,以验证其是否满足预期。简短的基准测试可能极具误导性。

5.3 读写大量数据

每次对 read() 和 write() 的函数调用都会导致对操作系统的一次系统调用。因此,执行这些函数调用需要大量 CPU 周期。因 此,缓冲区的大小必须足够大,以减少执行的系统调用次数。这对于带宽测试和高性能应用程序都是如此。

通常,128 kB 是每次函数调用的良好缓冲区大小。这意味着每次此类函数调用最多传输 128 kB。但是,这些函数调用允许 传输更少的数据。

务必注意,第 4.3 节中提到的示例程序 (streamread 和 streamwrite)不适合测量性能: 这些程序中的缓冲区大小是 128 字节(而非 kB)。这简化了示例,但使程序 对于性能测试来说太慢。

以下 shell 命令可用于速度检查(根据需要替换 /dev/xillybus_* 的名称):

dd if=/dev/zero of=/dev/xillybus_sink bs=128k
dd if=/dev/xillybus_source of=/dev/null bs=128k

这些命令将持续运行,直到被 CTRL-C 停止。添加“count=”以针对固定数据量进行测试。

5.4 注意 CPU 消耗

在高数据速率的应用中,计算机程序通常是瓶颈,而不一定是数据传输。

高估 CPU 能力是一个常见错误。与普遍看法相反,当数据速率超过 100-200 MB/s 时,即使最快的 CPU 也难以对数据做任 何有意义的事情。使用多线程可以提高性能,但需要这样做可能会令人惊讶。

有时缓冲区大小不当(如上所述)也会导致 CPU 消耗过高。

因此,密切关注 CPU 消耗非常重要。可以使用像“top”这样的实用程序来实现此目的。然而,该程序(以及类似的替代品) 的输出在多处理器核心的计算机上(即现今几乎所有计算机)可能具有误导性。例如,如果有四个处理器核心,25% 的 CPU 意味着什么?是 低 CPU 消耗,还是某个特定线程上的 100%?如果使用“top”,这取决于程序的版本。

另一点需要注意的是系统调用的处理时间如何测量和显示: 如果操作系统的开销拖慢了数据流,如何测 量?

检查这一点的一种简单方法是使用“time”实用程序。例如:

$ time dd if=/dev/zero of=/dev/null bs=128k count=100k
102400+0 records in
102400+0 records out
13421772800 bytes (13 GB) copied, 1.07802 s, 12.5 GB/s

real   0m1.080s
user    0m0.005s
sys    0m1.074s

底部“time”的输出表明“dd”完成所需的时间是 1.080 秒。在这段时间内,处理器在用户空间程序上花费了 5 毫秒,并在系统调 用上忙碌了 1.074 秒。因此在此特定示例中,很明显处理器几乎一直在忙于执行系统调用。这并不奇怪,因为“dd”在这里没有做任何事 情。

5.5 不要使读取和写入相互依赖

当需要双向通信时,一个常见错误是编写只有一个线程的计算机程序。该程序通常只有一个循环,同时进行读取和写 入: 在每次迭代中,向 FPGA 写入数据,然后从相反方向读取数据。

有时这样的程序没有问题,例如两个流在功能上独立。然而,此类程序的背后意图通常是 FPGA 应执行协处理。这种编程风 格基于一种误解,即程序应发送一部分数据进行处理,然后读回结果。因此,迭代构成了每部分数据的处理。

这种方法不仅低效,而且程序经常卡住。Xillybus Linux 主机应用程序编程指南的第 6.6 节对此主题进行了更详细的阐述,并提出了更合适的编程技术。

5.6 了解主机 RAM 的限制

这主要适用于嵌入式系统和/或使用 Revision XL / XXL IP 核时: 主板(或嵌入式处理器)与 DDR RAM 之间的 数据带宽有限。在计算机的常规使用中很少注意到此限制。但对于 Xillybus 的高要求应用,此限制可能成为瓶颈。

请记住,从 FPGA 到用户空间程序的每次数据传输都需要对 RAM 进行两次操作: 第一次操作是 FPGA 将数据 写入 DMA 缓冲区。第二次操作是驱动程序将此数据复制到用户空间程序可访问的缓冲区中。出于类似原因,当数据反向传输时也需要对 RAM 进行两次操作。

DMA 缓冲区和用户空间缓冲区之间的分离是操作系统要求的。所有使用 read() 和 write()(或类似函数调用)的 I/O 都必须以这种方式进 行。

例如,对 XL IP 核的测试预计在每个方向达到 3.5 GB/s,即总计 7 GB/s。然而,RAM 被访问了两次。因此 RAM 的带宽需 求是 14 GB/s。并非所有主板都能满足此要求。还要记住,主机同时使用 RAM 执行其他任务。

使用 Revision XXL 时,由于相同原因,即使是一个方向的简单测试也可能超过 RAM 的带宽能力。

5.7 足够大的 DMA 缓冲区

这很少成为问题,但仍值得一提: 如果在主机上为 DMA 缓冲区分配的 RAM 太少,可能会拖慢数据传输。原因 是主机被迫将数据流分成小段。这会导致浪费 CPU 周期。

所有 demo bundle 都具有足够的 DMA 内存用于性能测试。对于在 IP Core Factory 正确生成的 IP 核也是如此: 启用了“Autoset Internals”,且“Expected BW”反映了所需的数据带宽。“Buffering”应选择为 10 ms,尽管任何选项很可能都可以。

通常,这对于带宽测试来说已经足够: 至少四个 DMA 缓冲区,其总 RAM 量对应于 10 ms 内的数据传输量。 当然,必须考虑所需的数据传输速率。

5.8 使用正确的数据字宽

很明显,应用逻辑在每个 FPGA 内部时钟周期只能向 IP 核传输一个字的数据。因此,由于数据字宽和 bus_clk 频率的限 制,存在数据传输速率的上限。

除此之外,还存在与默认修订版(Revision A IP 核)相关的限制: 当字宽为 8 位或 16 位时,PCIe 的能力不如 字宽为 32 位时高效。因此,需要高性能的应用和测试应仅使用 32 位。这不适用于 Revision B 及更高版本的 IP 核。

从 Revision B 开始,字宽最大可达 256 位。字宽应至少与 PCIe 块的宽度一样宽。因此,对于数据带宽测试,需要以下数据 字宽:

  • 默认修订版(Revision A): 32 位。

  • Revision B: 至少 64 位。

  • Revision XL: 至少 128 位。

  • Revision XXL: 256 位。

如果数据字宽超过上述要求(可能时),通常可以获得稍好的结果。原因是改善了应用逻辑与 IP 核之间的数据传输。

5.9 因缓存同步导致的减速

此问题不适用于基于 x86 系列 CPU(32 位和 64 位)的计算机。那些在 Zynq 处理器的 AXI 总线上使用 Xillybus 的用户(例 如使用 Xillinux)也可以忽略此主题。

然而,几个嵌入式处理器在使用 DMA 缓冲区时需要显式同步缓存。这会严重拖慢与 CPU 外设的数据传输。

此问题并非 Xillybus 独有: 在基于 DMA 的所有 I/O(例如以太网、USB 和其他外设)中都能观察到类似行 为。

可以通过查看 CPU 消耗来揭示缓存导致的减速。如果 CPU 在系统调用状态(“time”实用程序的“sys”行输出)上花费了不合 理的时间,这可能表明问题是缓存引起的。这是因为 CPU 花费大量时间执行缓存同步。

然而,重要的是首先排除缓冲区过小的可能性(如上面第 5.35.7 节所述)。

x86 系列永远不会出现此问题,因为这些 CPU 具有一致性缓存。因此不需要缓存同步。Xillinux 也是如此,因为 IP 核通过 ACP 端口连接 到 CPU。

但是,当 Zynq 处理器通过 PCIe 总线使用 Xillybus 时,会出现此问题。其他几个嵌入式处理器也会受到影响,尤其是 ARM 处理器。

5.10 参数调优

demo bundle 中 PCIe 块的参数是为支持公布的数据传输速率而选择的。性能在带有 x86 系列 CPU 的典型计算机上进行了测试。

此外,在 IP Core Factory 生成的 IP 核通常不需要任何微调: 当启用“Autoset Internals”时,流很可能在性能和 FPGA 资源利用率之间达到最佳平衡。因此,每个流的请求数据传输速率得到保证。

因此,尝试微调 PCIe 块或 IP 核的参数几乎总是毫无意义的。对于默认修订版的 IP 核(Revision A),这种调整始终毫无意 义。如果此类调整提高了性能,则极有可能是应用逻辑或用户应用软件中存在缺陷。在这种情况下,修正该缺陷将获得更大的收益。

然而,在需要卓越性能的罕见场景中,可能需要稍微调整 PCIe 块的参数以达到所需的数据速率。这尤其适用于从主机到 FPGA 的流。自定义 Xillybus IP 核指南 的第 4.5 节讨论了如何执行此调整。

请注意,即使这种微调有益,修改的也不是 Xillybus IP 核的参数。仅调整 PCIe 块。通过调整 IP 核的参数来尝试提高数据传 输速率是一个常见错误。相反,问题几乎总是本章上面提到的某个问题。