VPP(Vector Packet Processing)和 Redis(Remote Dictionary Server,一种内存中的数据存储系统)一样,为了满足不同场景下的性能优化和功能扩展需求,自定义了底层数据结构。这些自定义的数据结构不仅简化了代码设计,还提高了内存效率。然而,为了真正发挥这些数据结构的优势,我们需要深入了解其内存分布的原理,并合理运用这些知识,从而提升转发性能。在公众号专栏《Vppinfra基础库代码详解》中我们介绍了基础库数据结构的使用场景和底层内存分布情况。在项目的开发中,强烈建议大家认真阅读源码。如果时间有限,也可以阅读我的公众号专栏文章。
在接触 VPP(Vector Packet Processing)之前,我曾在华为 NP(网络处理器)部门担任软件工程师。那时的主要职责是进行 DFX(Design for Excellence,通常指设计以提高可测试性、可维护性等)工作,具体任务包括定时检测芯片底层内部数据是否与期望值一致。若检测到不一致,则需要进行数据回刷或触发芯片的软重启,以恢复数据并确保业务正常运行。这类开发工作主要考验的是逻辑实现能力,通常只需要遵循芯片手册的要求编写代码即可。
DFX(Design for Testability) 在芯片设计中,“DFX”特指为了便于测试而进行的设计,尤其是为了方便对芯片内部状态进行检测和验证。这涉及到在设计阶段就考虑到如何方便地对芯片进行测试,确保芯片的功能正确性和可靠性。 FMEA(Failure Modes and Effects Analysis,失效模式与影响分析)是一种系统化的方法,用于识别产品设计中的潜在故障模式、评估风险、优化设计和改进过程控制。
而VPP(Vector Packet Processing)软件专为高性能网络处理而设计和开发。它对程序员的基本功提出了极高的要求,涉及对多种底层硬件特性的深入理解和应用,如 Cache、指令流水线、超标量处理(Superscalar)、SIMD(单指令多数据流)、分支预测及内存屏障等。此外,还需要熟练掌握并运用编译器和操作系统的基础知识。而我作为非科班出身,对这些知识一点不了解。通过阅读下面的书籍《深入浅出DPDK》才算入门。建议新入门的新伙伴一定要去阅读这本宝典。
下面就简单我看到一些问题。bihash结构使用:代码中为了保存pool内存池的索引,使用了bihash结构。而bihash中key是通过接口索引直接获取的固定数值X。在文章《VPPinfra---bihash简介》中了解bihash是为了解决高并发大容量数据而设计一种键值(key/value)存储数据结构。bihash使用前是需要预分配合理固定内存的,而且还需要额外内存存储hash桶数据。
Bihash实现的优势包括:
而当前场景只需要使用线性存储结构数据来实现就可以的。线性查找O(1)性能。
u32 *get_poolid_by_interface_vec;
在内核的邮件列表中提交一个patch中,作者重新调整了struct file内部字段的顺序,使经常访问的字段保持在同一个cache line上,可以在单numa下的高并发场景性能提升30%-50%。
建议各位详细阅读邮件内容: https://lore.kernel.org/lkml/ZHfKmG5RtgrMb6OT@dread.disaster.area/T/
patch修改代码如下:
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 21a981680856..01c55e3a1b96 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -962,23 +962,23 @@ struct file {
struct rcu_head f_rcuhead;
unsigned int f_iocb_flags;
};
- struct path f_path;
- struct inode *f_inode; /* cached value */
- const struct file_operations *f_op;
/*
* Protects f_ep, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
- atomic_long_t f_count;
- unsigned int f_flags;
fmode_t f_mode;
+ atomic_long_t f_count;
struct mutex f_pos_lock;
+ unsigned int f_flags;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
+ struct path f_path;
+ struct inode *f_inode; /* cached value */
+ const struct file_operations *f_op;
在 UnixBench 的系统调用测试中,由于伪共享(false sharing)导致了性能退化。在高并发测试场景中,锁和原子成员(包括 file::f_lock、file::f_count 和 file::f_pos_lock)高度竞争且频繁更新。perf c2c 识别出一个受影响的读取访问,即 file::f_op。为了防止伪共享,file 结构的布局进行了如下调整:
(A) f_lock、f_count 和 f_pos_lock 被放置在一起,共享同一个缓存行。(B) 大多数时候用于读取的成员(包括 f_path、f_inode 和 f_op)被放置到一个单独的缓存行。(C) f_mode 与 f_count 放置在一起,因为它们经常同时使用。
由此可见,数据结构的设计和定义对软件性能有着重大影响。请大家在定义数据结构和进行设计时务必慎重考虑。后期的性能优化往往需要花费数十倍甚至上百倍的努力才能取得显著提升。
本文分享自 DPDK VPP源码分析 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!