Skip to content

Lesson 12: DPDK Ring HTS模式

课程目标

学习DPDK Ring的HTS(Head-Tail Sync)同步模式,理解其适用场景,掌握Peek API的使用方法。


知识点

1. HTS模式简介

HTS(Head-Tail Sync):头尾同步模式,一种序列化的多生产者/多消费者同步方式。

核心特点

  • 同一时刻只允许一个线程操作Ring(像排队)
  • 避免MP/MC模式中的tail等待问题
  • 特别适合虚拟机/容器环境

2. 适用场景

虚拟机/容器环境 - 资源被调度器动态分配 ✅ 过度提交场景 - 线程数 > CPU核心数 ✅ 需要Peek API - 先看内容再决定是否取出 ✅ 需要稳定延迟 - 更可预测的延迟表现

3. 性能特点

场景HTS vs MP/MC
物理机慢10-20%
虚拟机/容器快30-50%

4. Peek API

Peek API:HTS模式独有的特性,允许"先看再取"。

使用场景

  • 条件过滤消息
  • 优先级队列实现
  • 根据消息内容决定是否处理

代码结构

目录结构

12-hts-ring/
├── CMakeLists.txt      # CMake构建配置
└── hts_ring.c          # HTS Ring示例代码

测试内容

示例代码包含4个测试:

  1. Test 1: HTS性能测试
  2. Test 2: HTS vs MP/MC性能对比
  3. Test 3: Peek API演示(HTS独有)
  4. Test 4: 多线程HTS测试

核心API

创建HTS Ring

c
#include <rte_ring.h>

/* 创建HTS模式Ring */
struct rte_ring *ring = rte_ring_create(
    "hts_ring",
    1024,                    // Ring大小(必须是2的幂)
    rte_socket_id(),         // NUMA socket
    RING_F_MP_HTS_ENQ |      // HTS生产者
    RING_F_MC_HTS_DEQ        // HTS消费者
);

使用通用API

HTS Ring使用标准的Ring API:

c
/* 入队 */
unsigned sent = rte_ring_enqueue_burst(ring, objs, n, NULL);

/* 出队 */
unsigned received = rte_ring_dequeue_burst(ring, objs, n, NULL);

Peek API(两阶段操作)

c
void *obj;

/* 阶段1:Peek - 查看队头元素(不移除) */
uint32_t n = rte_ring_dequeue_bulk_start(ring, &obj, 1, NULL);

if (n != 0) {
    /* 阶段2:决定是否取出 */
    if (should_accept(obj)) {
        /* 确认出队 */
        rte_ring_dequeue_finish(ring, 1);
        // 处理obj...
    } else {
        /* 取消出队(元素保留在Ring中) */
        rte_ring_dequeue_finish(ring, 0);
    }
}

编译和运行

编译

bash
# 在项目根目录
mkdir -p build
cd build
cmake ..
make hts_ring

运行

bash
# 基础运行(单核)
sudo ./bin/hts_ring -l 0 --no-pci

# 多核运行(推荐,用于测试4)
sudo ./bin/hts_ring -l 0-3 --no-pci

预期输出

╔════════════════════════════════════════════════╗
║   DPDK Ring HTS Mode Demo                     ║
║   (Head-Tail Sync Mode)                       ║
╚════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════╗
║   Test 1: HTS Mode Performance               ║
╚═══════════════════════════════════════════════╝

✓ Created HTS ring (size=1024)

Performance:
  Operations: 1000000
  Time: 0.082 seconds
  Throughput: 12.20 Mpps

╔═══════════════════════════════════════════════╗
║   Test 2: HTS vs MP/MC Comparison            ║
╚═══════════════════════════════════════════════╝

✓ Created HTS and MP/MC rings

Testing HTS mode...
Testing MP/MC mode...

┌────────────┬──────────┬──────────────┐
│   Mode     │   Mpps   │  Relative    │
├────────────┼──────────┼──────────────┤
│   HTS      │  12.20   │    100.0%    │
│   MP/MC    │  14.35   │    117.6%    │
└────────────┴──────────┴──────────────┘

💡 HTS is 15.0% slower (normal on physical machines)

╔═══════════════════════════════════════════════╗
║   Test 3: Peek API (HTS Only)                ║
╚═══════════════════════════════════════════════╝

✓ Created HTS ring for Peek API test
✓ Enqueued 20 messages with different priorities

Using Peek API to filter messages (only accept priority 0 and 1):
──────────────────────────────────────────────────────────
  [Peek #1] Seq=0, Priority=0 → ✓ Accept
  [Peek #2] Seq=1, Priority=1 → ✓ Accept
  [Peek #3] Seq=2, Priority=2 → ✗ Reject (stop)
──────────────────────────────────────────────────────────

Peek API Results:
  Peeked:   3 messages
  Accepted: 2 messages (priority 0-1)
  Rejected: 1 messages (priority 2)
  Remaining in ring: 17 messages

💡 Peek API allows conditional dequeue:
   - Look at the message first
   - Decide whether to take it or leave it
   - Only supported by HTS and SP/SC modes

╔═══════════════════════════════════════════════╗
║   Test 4: Multi-thread HTS Test              ║
╚═══════════════════════════════════════════════╝

✓ Created HTS ring for multi-thread test
  Available lcores: 4

  [Lcore 1] Worker started
  [Lcore 2] Worker started
  [Lcore 3] Worker started
  [Lcore 1] Worker finished (enqueued 100)
  [Lcore 2] Worker finished (enqueued 100)
  [Lcore 3] Worker finished (enqueued 100)

✓ All 3 workers completed
  Final ring count: 0

╔════════════════════════════════════════════════╗
║   All Tests Completed                          ║
╚════════════════════════════════════════════════╝

Key Takeaways:
  1. HTS is 10-20% slower than MP/MC on physical machines
  2. HTS is faster in VM/container environments (overcommit)
  3. Peek API is unique to HTS and SP/SC modes
  4. HTS provides more predictable latency

测试解析

Test 1: HTS性能测试

测试HTS模式的基本性能,通过100万次入队/出队操作测量吞吐量。

关键代码

c
for (i = 0; i < TEST_COUNT / 32; i++) {
    rte_ring_enqueue_burst(hts_ring, objs, 32, NULL);
    rte_ring_dequeue_burst(hts_ring, objs, 32, NULL);
}

Test 2: HTS vs MP/MC对比

对比HTS和MP/MC模式的性能差异。

预期结果

  • 物理机:MP/MC快10-20%
  • 虚拟机/容器:HTS快30-50%

Test 3: Peek API演示

演示Peek API的条件式出队功能。

场景:根据消息优先级决定是否处理

  • Priority 0-1:接受并处理
  • Priority 2:拒绝并停止

核心逻辑

c
ret = rte_ring_dequeue_bulk_start(ring, &obj, 1, NULL);
if (msg->priority <= 1) {
    rte_ring_dequeue_finish(ring, 1);  // 确认取出
} else {
    rte_ring_dequeue_finish(ring, 0);  // 取消,保留在Ring中
}

Test 4: 多线程HTS测试

多个worker线程同时对HTS Ring进行入队/出队操作,验证HTS的同步正确性。


常见问题

Q1: HTS比MP/MC慢,为什么还要用?

:在特定场景下HTS更优:

  • 虚拟机/容器:HTS更快(避免tail等待)
  • 需要Peek API:只有HTS和SP/SC支持
  • 延迟敏感:HTS延迟更可预测

Q2: Peek API的典型应用?

  1. 消息过滤:跳过不感兴趣的消息
  2. 优先级队列:实现复杂的调度策略
  3. 条件处理:根据系统状态决定是否处理
  4. 流量控制:根据负载动态调整接收

Q3: 如何选择Ring模式?

决策树

是否单生产者单消费者?
  └─> 是:SP/SC(最快)

是否需要Peek API?
  └─> 是:HTS

是否运行在虚拟机/容器?
  └─> 是:HTS
  └─> 否:MP/MC(物理机最快)

Q4: Peek API的性能开销?

  • Peek本身开销很小
  • 如果频繁"看了又不取"会影响性能
  • 建议:如果大部分消息都会处理,直接用dequeue

Q5: 可以混合使用不同模式吗?

:不能。Ring创建时确定模式,之后不能更改。


实践练习

练习1:实现优先级队列

使用Peek API实现一个简单的优先级队列:

  • 只处理高优先级消息(priority < 5)
  • 低优先级消息留在队列中

练习2:性能测试

在虚拟机/容器环境中运行测试,观察HTS vs MP/MC性能差异。

练习3:条件过滤

实现一个消息过滤器:

  • 使用Peek API查看消息
  • 根据消息类型决定是否处理
  • 统计accept/reject比例

总结

核心要点

  1. HTS特点:同一时刻只允许一个线程操作
  2. 适用场景:虚拟机/容器、需要Peek API
  3. 性能权衡:物理机慢10-20%,虚拟机快30-50%
  4. Peek API:HTS独有,实现条件式出队

选择建议

场景推荐模式
单生产者单消费者SP/SC
物理机高吞吐MP/MC
虚拟机/容器HTS
需要Peek APIHTS

参考资源


下一课预告:RTS模式(Relaxed Tail Sync)- HTS的优化版本

Released under the MIT License.