目录

Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)

Ens ̄o: A Streaming Interface for NIC-Application Communication

NIC interface 相关的论文,hardware offloads, steaming interface

2023 OSDI best paper,我读起来觉得很困难,尤其是不理解其中的一些设计,这篇论文工作量应该是非常高的,值得好好读

之前看得都是 kernel bypass (XDP, DPDK) 又或者是 NIC offload,但 NIC 到 软件 的缓冲区和包传递其实也存在瓶颈

核心思想是固定大小的包缓冲区变成了流缓冲区,省去了 metadata 传输等等,对于小包传输带宽提升明显,enso 用单核就能跑到很高的带宽

2023 年操作系统设计与实现研讨会(OSDI)有哪些值得关注的文章? - ChenKuGai 的回答 - 知乎 https://www.zhihu.com/question/591516372/answer/3506857114

知乎的解答很不错

Abstract

今天,网卡和软件之间的大多数通信都涉及到交换固定大小的数据包缓冲区。这种分组接口是为一个时代而设计的,当时 NIC 实现了很少的卸载,软件实现了在应用程序数据和数据包之间进行转换的逻辑。然而,NIC 和网络软件都已经发展: 现代 NIC 实现了硬件卸载,例如 TSO、 LRO 和序列化卸载,可以更有效地在应用程序数据和数据包之间进行转换。此外,现代软件越来越多地批处理网络 I/O,以减少开销。这些变化导致了分组接口(假设网卡和软件交换固定大小的缓冲区)与现代网卡提供并由现代软件使用的特性之间的不匹配。接口和数据之间的这种不一致性增加了软件复杂性和 I/O 开销,从而限制了通信性能。本文提出了一种新的流式 NIC-to-software 接口 Ens o,旨在更好地支持当今 NIC 和软件的交互方式。在其核心,Ens o 避免固定大小的缓冲区,而是将通信结构设计为一个流,可用于发送任意大小的数据。我们展示了这个更改减少了软件开销,降低了 PCIe 带宽需求,并导致更少的缓存丢失。这些改进使得基于 Ens o 的网卡能够在 100Gbps 的链路上使用单个核心的最小数据包(转发速度为 148.8 Mpps) ,提高高性能网络应用的吞吐量 1.5-6 倍,降低延迟高达 43%

hardware offloads, serialization offloads

避免固定大小的缓冲区

Introduction

NIC offloads allow the NIC to perform common tasks (e.g., segmentation) previously implemented in software; 更高效的网络 I/O 库和接口,包括 DPDK 和 XDP,允许应用程序减少网络栈的处理

大多数 NIC 目前提供了一个接口,其中所有软件和 NIC 之间的通信都需要发送(和接收)一系列固定大小的缓冲区,我们在本文中称之为数据包缓冲区。

首先,许多 NIC 卸载,如 TCP 分段卸载(TSO)[20, 39]、大接收卸载(LRO)[14]、序列化卸载[44, 71, 86]和传输卸载[3, 14, 27, 77],它们的输入(和输出)可以跨越多个数据包并且大小不一。在使用这些卸载与数据包化接口时,软件在与 NIC 通信时必须不必要地将数据分割(和重新组合)到多个数据包缓冲区中。

Packetized abstraction: 尽管在软件始终需要交换 MTU 大小的数据包时,采用固定大小的缓冲区效果合理,但在使用更高级别的抽象(如应用程序级消息(例如,RPC)、字节流)或更简单的卸载(如 LRO)时,它变得笨拙。

Poor cache interaction: 由于数据包化接口将传入和传出的数据分散在内存中,它限制了预取器和其他需要预测软件将访问的下一个内存地址的 CPU 优化的有效性——我们称之为混乱的内存访问。 如对象缓存[9, 57]和键值存储[4, 52]。

Metadata overhead: 由于数据包化接口依赖于每个数据包的元数据,它将相当一部分 PCIe 带宽用于传输元数据——在使用小消息时高达 39%的可用带宽。这导致处理小请求的应用程序受到 PCIe 的瓶颈限制,我们在实现 Google 的 Maglev 负载均衡器[23]时观察到了由于 PCIe 瓶颈导致的可扩展性问题。

负载均衡器会受到影响?

在本文中,我们提出了 Ensō,一种新的 NIC-应用程序通信接口,它摒弃了较低级别的数据包概念。相反,Ensō 提供了一种流式抽象,NIC 和应用程序可以使用它来通信任意大小的数据块。这样做不仅使 NIC 和应用程序能够使用更适合各自实现的功能的任意数据格式,而且还摆脱了数据包化接口中存在的性能问题。因为 Ensō 对数据格式本身没有任何假设,所以可以根据应用程序和 NIC 上启用的卸载进行重新利用。例如,如果 NIC 只负责复用/解复用,它可以使用 Ensō 来传输原始数据包;如果 NIC 也了解应用程序级消息,它可以使用 Ensō 将整个消息和 RPC 传递给应用程序;如果 NIC 实现了传输协议,如 TCP,它可以使用 Ensō 使用字节流与应用程序通信。

流式、字节流

为了提供流式抽象,Ensō 用包含数据的环形缓冲区替换了当前 NIC 接口使用的包含描述符的环形缓冲区。NIC 和软件通过将数据附加到这些环形缓冲区进行通信。Ensō 将缓冲区视为不透明的数据,并且不对它们的内容、结构或大小施加任何要求,从而允许它们被用来传输任意大小的数据,其大小可以和环形缓冲区本身一样大。Ensō 还显著减少了由于元数据导致的 PCIe 带宽开销,因为它能够对写入同一缓冲区的多个数据块进行聚合通知。最后,它 enables better use of CPU prefetcher 以掩盖内存延迟,从而进一步提高应用程序性能。

ring buffer

为了理解其性能,我们使用基于 FPGA 的 SmartNIC 完全实现了 Ensō。我们在第 5 节中描述了我们的硬件和软件实现,以及根据 NIC 提供的功能 Ensō 的使用方式在第 6 节中。在第 7 节中,我们展示了 Ensō 的评估,包括它在四个应用程序中的使用:Maglev 负载均衡器[23]、基于 NitroSketch 的网络遥测应用程序[54]、MICA 键值存储[52],以及受 AWS CloudWatch Logs[6]启发的日志监控器。我们还实现了一个软件数据包生成器,我们在大多数实验中使用它。我们观察到与 DPDK 没有硬件卸载的实现相比,Maglev 的速度提高了 6 倍,MICA 的速度提高了 1.47 倍,而。

实现了一个硬件?还是硬件上的软件?为什么和 dpdk 非硬件卸载比,是不是不太对照

Background and Motivation

数据包化 NIC 接口决定了几乎所有高性能网络库提供的 API,包括 io_uring[15]、DPDK[19]和 netmap[73]

Packetized NIC Interface

NIC 卸载允许 NIC 执行以前在软件中实现的常见任务(例如,分段);更高效的网络 I/O 库和接口,包括 DPDK 和 XDP,允许应用程序减少网络栈的处理。

但 NIC 到软件的接口几十年来一直没有变化。

数据包化 NIC 接口的一个核心设计选择是为每个数据包放置一个专用的数据包缓冲区。

缓冲区通常被设置为可以容纳 MTU 大小的数据包

https://s2.loli.net/2024/09/30/yuNxgbGVeaURAQM.png

图 1 显示了一个数据包化 NIC 接口被用来从 NIC 上的特定硬件队列接收四个数据包的示例。NIC 队列与一组 NIC 寄存器相关联,这些寄存器可以用来控制接收(RX)描述符环形缓冲区和传输(TX)描述符环形缓冲区。在能够接收数据包之前,软件通过在 RX 描述符环形缓冲区中排队指向每个缓冲区的描述符,通知 NIC 其缓冲区池中多个可用缓冲区的地址。然后,NIC 可以使用 DMA 将传入的数据包数据写入下一个可用的数据包缓冲区,并排队更新的描述符,包含数据包大小等元数据。重要的是,NIC 还在描述符中设置了一个“标志 flag”位,向软件发出信号,表明该缓冲区已到达数据包。观察到描述符下的头部指针的通知位,软件然后可以增加头部指针。

传输过程也类似:发送软件为准备传输的数据包缓冲区组装一组描述符,并将描述符(而不是数据包本身)复制到 TX 环形缓冲区;描述符中的标志位现在用于向 NIC 发出信号,表明已经传输(而不是接收)了一个数据包。

为每个数据包分配缓冲区的一个主要好处是,可以在软件中高效地进行复用/解复用。

然而,现代高性能软件栈的使用模式看起来非常不同。不是有一个软件实体(例如,内核,软件交换机)调解对 NIC 的访问,可能有多个线程或进程直接访问 NIC(即,内核绕过)。然后 NIC 承担了解复用的所有责任,使用例如 RSS[82]、Intel 的 Flow Director[39],或者(对于非常丰富的交换模型)Microsoft 的 AccelNet[28]。在这种设置中,数据包化 NIC 接口的复用/解复用能力没有提供任何额外的价值。

Issues with a Packetized Interface

尽管许多高性能应用程序今天从数据包化接口中获得的好处很少,但它们仍然需要为伴随而来的开销付出代价。将 NIC 和应用程序之间的数据通信硬塞进固定大小的块中,会导致 CPU 缓存和 PCIe 带宽在小请求上的使用效率低下,以及由于应用程序依赖于大消息或字节流而导致的额外数据复制。

主要是面对小请求吗,不知道后续的 evaluation 是不是也都是小请求

Chaotic Memory Access: 基于 DPDK 的简单 ping/pong 程序,主要是由于 L1 和 L2 缓存未命中。像键值存储[4, 52]或数据包处理器[18]这样的应用程序在数据访问上表现出非常高的空间-时间局部性。实际上,先前的工作[55, 81]已经反复证明,数据包处理应用程序的工作集大小经常超出专用于 DDIO[35]的缓存空间,抵消了这种硬件优化的好处,将 I/O 数据直接带入缓存。正如我们在第 7.2.3 节中详细讨论的,使用一种促进顺序内存访问的不同 NIC 接口可以将 L1d 缓存的未命中率从 6%降低到 0.2%,将 L2 缓存的未命中率从 55%降低到 9%。

这里的缓存是 cpu 的吗

Metadata Bandwidth Overhead: 高达 39%的 CPU 到 NIC 互连带宽用于传输描述符

总结:通过为每个数据包配对一个单独的描述符,数据包化 NIC 接口非常适合以前一代需要在软件中实现复用的高吞吐量网络应用程序。然而,对于今天的高性能应用程序,它引入了不必要的性能开销。

Enso Overview

网络接口卡(NIC)与应用程序通信的流式接口

  1. 灵活性,允许它用于不同类别的卸载操作,这些操作在不同的网络层级上运行,并且具有不同的数据大小;
  2. 低软件开销,减少应用程序在通信上需要花费的周期数;以及
  3. 硬件简单性,使得在商品 NIC 上的实际实现成为可能。

这是一种新的缓冲区抽象,允许应用程序和 NIC 交换任意大小的数据块,就像读写一个无界内存缓冲区一样。与数据包化接口使用的环形缓冲区不同(这些缓冲区包含分散的数据包缓冲区的描述符),Ens ̄o Pipe 是实现为数据环形缓冲区,包含实际的数据包数据

都是环,区别在哪呢

High-level operation: 最初,Pipes 是空的,HeadSW 和 TailNIC 指向缓冲区 1 中的同一位置。当 NIC 接收到消息时,它使用 DMA 将它们排队到 Pipes 拥有的连续内存中。在图中,NIC 在 Pipe A 的内存中排队了两个消息,在 Pipe B 的内存中排队了三个。NIC 通过在通知缓冲区中排队两个通知(每个 Enso Pipe 一个)来通知软件。软件使用这些通知来推进 TailNIC 并处理消息。一旦消息被处理,软件就写入一个内存映射 I/O(MMIO)寄存器(推进 HeadSW)来通知 NIC——允许内存被后续消息重用 3。发送消息是对称的,除了最后一步:NIC 通过覆盖 CPU 用来通知 NIC 有消息可供传输的通知来通知软件消息已被传输。

https://s2.loli.net/2024/10/01/9mB5fCsVkbIX1ZU.png

感觉过程和 ring buffer 没啥区别?

Ens ̄o Pipe’s flexibility:尽管图 3 显示了发送消息的步骤,但由于 Ens ̄o Pipes 是不透明的,它们可以用来传输任意大小的数据块。这些可以是原始数据包,由多个 MTU 大小的数据包组成的消息,甚至是无界字节流。数据的格式由应用程序和在 NIC 上运行的卸载决定。此外,Ens ̄o Pipes 的不透明性意味着它们可以映射到应用程序内存空间内的任何固定内存。因此,通过将 RX 和 TX Ens ̄o Pipes 映射到同一区域,网络功能和其他转发应用程序可以避免复制数据包。在我们的评估(§7)中,我们在实现 Maglev 和网络遥测应用程序时使用了这种方法。

Performance advantages of an Ens ̄o Pipe: pipes 允许应用程序顺序读写 I/O 数据,从而避免了混乱的内存访问

Challenges: 尽管实现数据传输的环形缓冲区本身是一个简单的想法,但协调 CPU 和 NIC 之间的通知以更新头部和尾部指针却是一个挑战。

Efficient coordination:

在§4.1 中,我们讨论了通知的简单方法如何会使 MMIO 和 DMA 的最坏情况性能受到压力。特别是,对同一内存地址的并发访问可能会在 CPU 和 NIC 之间产生缓存争用。Ens ̄o 使用专用的通知缓冲区来同步头部和尾部指针的更新;当与批处理和多队列处理结合使用时,通知缓冲区方法减少了缓存争用的威胁。

Notification pacing: Pipes 被设计成可以合并多个数据包的通知,减少了 CPU 和 NIC 之间传输的元数据量。然而,决定何时发送通知仍然很重要:如果发送得太频繁,它们会浪费 PCIe 带宽并增加软件开销,但如果发送得太不频繁,核心可能会闲置等待通知,从而降低吞吐量。Ens ̄o 包括两种机制,反应性通知和通知预取(§4.2),它们控制何时发送通知。这些机制是自然自适应的,即它们在不限制吞吐量的情况下最小化发送的通知数量,并且可以在不增加硬件复杂性的情况下实现。

同一地址访问,缓存争用,是什么情况

enso 使用了多队列

流计算:合并多个包?

Low hardware complexity and state: Ens ̄o 的设计涉及硬件和软件,我们必须小心,不要用软件的简单性来换取硬件的复杂性。Ens ̄o 倾向于需要很少 NIC 状态的协调机制。我们的目标是设计一个简单且易于并行化的系统

Target applications: 流式接口,针对按顺序处理接收数据的情况进行优化。我们的评估(§7)表明,这涵盖了广泛的网络密集型应用程序

由此产生的设计不适合需要复用和解复用数据包的应用程序(例如,像 Open vSwitch [67]和 BESS [32]这样的虚拟交换机),因为这些应用程序需要额外的复制。然而,令人惊讶的是,即使需要这样的额外复制,Ens ̄o 的性能也超过了数据包化接口

为什么不适合 multiplex and demultiplex

但是为什么性能又好很多?硬件 Offload 带来的显著提升吗

Efficient Notifications

efficiently coordinating Enso Pipes between the CPU and the NIC

Efficient Ens ̄o Pipe Coordination

How should software and the NIC communicate pointer updates?

在数据包化 NIC 接口使用的描述符环形缓冲区中,软件使用 MMIO 写入来向 NIC 通信指针更新,而 NIC 通过描述符缓冲区本身的内联信号来通信指针更新[25, 39],避免了 MMIO 读取的开销。因为描述符的格式由 NIC 定义,NIC 可以在每个描述符中专门设置一个“标志信号”来信号描述符是有效的。然后,软件可以轮询下一个描述符,直到其标志变为有效。这样,NIC 就不需要明确地告诉软件指针更新。 不幸的是,我们不能对 Ens ̄o 使用这种方法。虽然 Ens ̄o Pipes 仍然可以使用 MMIO 写入来更新软件的指针,但我们不能在 Ens ̄o Pipe 本身中嵌入内联信号,因为我们不对数据施加任何结构。 我们考虑了几种设计选项来通信指针更新。我们在这里关注 illuminating rejected design here:在主内存中 NIC 和软件之间共享一个地址。对于每个 Pipe,我们可能有一个专用的内存地址,NIC 在其中写入最新的 TailNIC。然后,软件可以轮询这个地址来确定最新的值。不幸的是,这种方法会导致争用,因为每次 NIC 写入内存时,CPU 上的缓存条目就会被使无效。如果软件继续轮询同一缓存行,由此产生的争用会将吞吐量降低几个数量级:我们在使用小传输时使用这种方法测量到的吞吐量低于 5 Gbps。我们在附录中讨论了其他被拒绝的方法。

不对数据施加任何结构?为什么,因为是流式?

共享一个地址,导致争用,为什么 NIC 写内存 CPU 缓存会失效?缓存一致性?缓存行无效?为什么不直接写入内存,DMA 呢?

Pipe coordinate 还是指针的位置问题。

Notification Buffer

Ens ̄o 使用通知缓冲区来通信指针更新。虽然通知缓冲区本身的结构并不能解决缓存争用挑战,但当它与批量通知结合使用,并且用于聚合多个 Ens ̄o Pipes 的通知更新时,这种方法可以防止 CPU 忙等待共享缓存行,从而避免争用引起的减速。

https://s2.loli.net/2024/10/01/NCd5M4j6Dn9WV3v.png

一个通知,指示多个连续数据库

批量/合并通知,消费通知后 MMIO 写入指针,使用通知缓冲区来更新指针。

还是不懂为什么不直接用 DMA

Multiplexing and Scaling

在单个线程内:为了让单个线程有效地访问多个 Ens ̄o Pipes,我们将多个 Ens ̄o Pipes 与同一个通知缓冲区关联。因此,软件可以探测单个通知缓冲区来检索多个 Ens ̄o Pipes 的更新。这避免了需要轮询多个队列的已知可扩展性问题

在多个线程之间:为了让多个线程独立地发送和接收数据,Ens ̄o 支持多个通知缓冲区。每个线程可以使用专用的通知缓冲区,避免了昂贵的同步原语。在设置新的 Ens ̄o Pipe 时,软件告诉 NIC 它与哪个通知缓冲区关联。因此,NIC 知道要向哪个通知缓冲区发送通知。

在多个应用程序之间:除了使用独立的的通知缓冲区,Ens ̄o 确保应用程序只访问它们自己的 Ens ̄o Pipes 和通知缓冲区的子集。每个队列的 MMIO 指针寄存器对保持在其自己的专用页面对齐的内存块中[22]。这让内核可以将指针寄存器以每个队列的粒度映射到请求它的应用程序的地址空间。

那 pipe 是怎么分的,还需要元数据吗,到底多少个 pipes 呢,是多个应用就多个 pipe 吗,

Notifications: Contention and Overhead

允许多个 Ens ̄o Pipes 共享同一个通知队列(§4.1.2),并且只有对于更大的数据批次才有通知到达(§4.2)自然地通过保持 NIC 在更新通知缓冲区时“领先”于 CPU 来防止争用,并且也减少了通信这些通知的 PCIe 开销。当 CPU 读取一个 Ens ̄o Pipe 的数据时,NIC 正在为后续的 Ens ̄o Pipes 写入新的条目。因为 CPU 正在处理更大的数据批次,所以在需要检查通知缓冲区之前,它会更忙更长时间。因此,随着线速的提高,两者不太可能同时访问同一个缓存行。

这里 FIFO 队列的 tail latency 应该是比较低的

而且必须注册连续的地址空间,类似 RDMA?

后续的没来得及看了