VPP多架构处理器支持

对于转发层面的关键节点(node),VPP针对处理器架构编译多份代码,在运行时检测处理器架构,动态确定使用的代码分支。VPP提供两种对多处理器架构的支持,除了节点函数外,还可指定任意函数支持多架构。

node节点多架构

编译系统将node函数所在文件编译多次,每次使用不同的编译选项,生成多个node函数版本。每个node的构造函数(constructor)根据处理器硬件选择对应的版本,构造函数将结构vlib_node_registration_t的成员function附上选择的函数版本。

VLIB_NODE_FN (ip4_rewrite_node) (vlib_main_t * vm, vlib_node_runtime_t * node,vlib_frame_t * frame)
{if (adj_are_counters_enabled ())return ip4_rewrite_inline (vm, node, frame, 1, 0, 0);elsereturn ip4_rewrite_inline (vm, node, frame, 0, 0, 0);
}

所以在使用宏VLIB_REGISTER_NODE定义节点时(初始化vlib_node_registration_t结构),不要指定function成员。

VLIB_REGISTER_NODE (ip4_rewrite_node) = {.name = "ip4-rewrite",.vector_size = sizeof (u32),...
};

node函数所在文件中,不重要的函数,如错误字符串定义和报文追踪函数(trace),不需要支持多处理器架构。使用"#ifndef CLIB_MARCH_VARIANT…#endif"来去除。

#ifndef CLIB_MARCH_VARIANT
/* Common trace function for all ip4-forward next nodes. */
void
ip4_forward_next_trace (vlib_main_t * vm,vlib_node_runtime_t * node,vlib_frame_t * frame, vlib_rx_or_tx_t which_adj_index)
{}
#endif

CMakeLists.txt文件中需要将ip/ip4_forward.c文件同时添加到VNET_SOURCES和VNET_MULTIARCH_SOURCES链表,add_vpp_library将对其处理。

list(APPEND VNET_SOURCESip/ip4_forward.c
)
list(APPEND VNET_MULTIARCH_SOURCESip/ip4_forward.c
)add_vpp_library(vnetSOURCES ${VNET_SOURCES}MULTIARCH_SOURCES ${VNET_MULTIARCH_SOURCES}INSTALL_HEADERS ${VNET_HEADERS}

节点函数调用的子函数(ip4_rewrite_inline),需要使用always_inline修饰符。否则,编译器很可能不会生成多架构代码。可以在vpp运行时用perf top命令检查,比如当前处理器支持avx2,可以看到许多类似"xxx_node_fn_avx2"名字的节点处理函数。如果某个节点的名称为"xxx_inline.isra.1",表明其很可能没有使用always_inline修饰符,而是使用了static inline。

always_inline uword
ip4_rewrite_inline (vlib_main_t *vm, vlib_node_runtime_t *node,vlib_frame_t *frame, int do_counters, int is_midchain,int is_mcast)
{

任意函数多架构

可以使用CLIB_MARCH_FN修饰需要支持多架构的函数,在调用函数时使用CLIB_MARCH_FN_SELECT选择特定的版本。

CLIB_MARCH_FN (svm_fifo_copy_to_chunk, void, svm_fifo_t *f,svm_fifo_chunk_t *c, u32 tail_idx, const u8 *src, u32 len,fs_sptr_t *last)
{

类似于node节点的多架构支持,CLIB_MARCH_FN宏所在源文件也需要多次编译。比如,在生成的vpp程序中,可能出现多个此函数的版本:svm_fifo_copy_to_chunk_avx2、svm_fifo_copy_to_chunk_avx512等。

函数svm_fifo_copy_to_chunk调用了以上的svm_fifo_copy_to_chunk函数,由于svm_fifo_copy_to_chunk为多架构版本,svm_fifo_copy_to_chunk函数不需要进行多架构编译,使用#ifndef CLIB_MARCH_VARIANT禁止多架构编译功能。

CLIB_MARCH_FN_SELECT的开销相等于间接函数调用。

#ifndef CLIB_MARCH_VARIANTstatic inline void
svm_fifo_copy_to_chunk (svm_fifo_t *f, svm_fifo_chunk_t *c, u32 tail_idx,const u8 *src, u32 len, fs_sptr_t *last)
{CLIB_MARCH_FN_SELECT (svm_fifo_copy_to_chunk) (f, c, tail_idx, src, len,last);
}
#endif

另外,还可以使用CLIB_MARCH_FN_REGISTRATION和CLIB_MARCH_FN_POINTER来实现函数的多架构编译。

多处理器架构

在文件vpp/src/cmake/cpu.cmake文件中,加入对多架构的支持(add_vpp_march_variant)。对x86_64处理器,默认使用corei7和corei7-avx编译选项。多架构支持增加了haswell、tremont、skylake和icelake四种架构。默认关闭了tremount架构的支持(OFF)。skylake和icelake架构的支持事编译器而定。

if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")set(VPP_DEFAULT_MARCH_FLAGS -march=corei7 -mtune=corei7-avx)add_vpp_march_variant(hswFLAGS -march=haswell -mtune=haswell)add_vpp_march_variant(trmFLAGS -march=tremont -mtune=tremontOFF)if (GNU_ASSEMBLER_AVX512_BUG)message(WARNING "AVX-512 multiarch variant(s) disabled due to GNU Assembler bug")else()add_vpp_march_variant(skxFLAGS -march=skylake-avx512 -mtune=skylake-avx512 -mprefer-vector-width=256)add_vpp_march_variant(iclFLAGS -march=icelake-client -mtune=icelake-client -mprefer-vector-width=512)endif()

使用cmake内置函数check_c_compiler_flag检查C编译器是否支持指定的flag,对于支持的flag添加到MARCH_VARIANTS,比如最终的值可能为:(hsw; -march=haswell -mtune=haswell;skx; -march=skylake-avx512 -mtune=skylake-avx512 -mprefer-vector-width=256)。MARCH_VARIANTS_DISABLED的值为:(trm; -march=tremont -mtune=tremont)。

check_c_compiler_flag函数参见https://cmake.org/cmake/help/latest/module/CheckCCompilerFlag.html

macro(add_vpp_march_variant v)if(ARG_FLAGS)set(flags_ok 1)set(fs "")foreach(f ${ARG_FLAGS})string(APPEND fs " ${f}")string(REGEX REPLACE "[-=+]" "_" sfx ${f})if(NOT DEFINED compiler_flag${sfx})check_c_compiler_flag(${f} compiler_flag${sfx})endif()if(NOT compiler_flag${sfx})unset(flags_ok)endif()endforeach()if(flags_ok)string(TOUPPER ${v} uv)if (VPP_MARCH_VARIANT_${uv})list(APPEND MARCH_VARIANTS "${v}\;${fs}")else()list(APPEND MARCH_VARIANTS_DISABLED "${v}\;${fs}")endif()endif()endif()
endmacro()

多架构源文件

使用add_vpp_library添加多架构源文件,最终调用函数vpp_library_set_multiarch_sources。

macro(add_vpp_library lib)cmake_parse_arguments(ARG"LTO""COMPONENT""SOURCES;MULTIARCH_SOURCES;API_FILES;LINK_LIBRARIES;INSTALL_HEADERS;DEPENDS"${ARGN})if(ARG_MULTIARCH_SOURCES)vpp_library_set_multiarch_sources(${lib} DEPENDS ${ARG_DEPENDS} SOURCES ${ARG_MULTIARCH_SOURCES})endif()

为每个架构生成不同的库文件,对于vnet,生成基础的vnet,以及vnet_skx和vnet_hsw。

macro(vpp_library_set_multiarch_sources lib)cmake_parse_arguments(ARG"""""SOURCES;DEPENDS;FORCE_ON"${ARGN})set(VARIANTS "${MARCH_VARIANTS}")foreach(V ${VARIANTS})list(GET V 0 VARIANT)list(GET V 1 VARIANT_FLAGS)set(l ${lib}_${VARIANT})add_library(${l} OBJECT ${ARG_SOURCES})set_target_properties(${l} PROPERTIES POSITION_INDEPENDENT_CODE ON)target_compile_definitions(${l} PUBLIC CLIB_MARCH_VARIANT=${VARIANT})separate_arguments(VARIANT_FLAGS)target_compile_options(${l} PUBLIC ${VARIANT_FLAGS})target_sources(${lib} PRIVATE $<TARGET_OBJECTS:${l}>)endforeach()
endmacro()

如下生成的不同架构的目标文件。vnet_objs.dir目录包括所有源文件的目标文件,而以后缀hsw和skx的目录仅包括需要多架构支持的源文件的目标文件。

$ ls build-root/build-vpp-native/vpp/CMakeFiles/vnet/CMakeFiles/vnet_objs.dir/ip/
ip_in_out_acl.c.o ip4_punt_drop.c.o   ip6_punt_drop.c.o       ip_path_mtu_node.c.o  reass
ip6_forward.c.o   ip4_forward.c.o     ip6_hop_by_hop.c.o      ip4_input.c.o     ip6_input.c.o                        
ip4_mtrie.c.o     ip6_link.c.o        ip_frag.c.o             ip_types.c.o      ...
$ 
$ ls build-root/build-vpp-native/vpp/CMakeFiles/vnet/CMakeFiles/vnet_hsw.dir/ip
ip4_forward.c.o  ip4_punt_drop.c.o  ip6_hop_by_hop.c.o  ip6_punt_drop.c.o  ip_path_mtu_node.c.o  reass
ip4_input.c.o    ip6_forward.c.o    ip6_input.c.o       ip_in_out_acl.c.o  punt_node.c.o
$
$ 
$ ls build-root/build-vpp-native/vpp/CMakeFiles/vnet/CMakeFiles/vnet_skx.dir/ip
ip4_forward.c.o  ip4_punt_drop.c.o  ip6_hop_by_hop.c.o  ip6_punt_drop.c.o  ip_path_mtu_node.c.o  reass
ip4_input.c.o    ip6_forward.c.o    ip6_input.c.o       ip_in_out_acl.c.o  punt_node.c.o

反编译,可看到如下的三个函数,分别为基础node函数ip4_lookup_node_fn,和skx以及hsw结构版本的函数。

0000000000002ad0 <ip4_lookup_node_fn>:
ip4_lookup_node_fn():2ad0:   41 57                   push   %r15000000000000e5d0 <ip4_lookup_node_fn_skx>:
ip4_lookup_node_fn_skx():e5d0:   55                      push   %rbp000000000000f010 <ip4_lookup_node_fn_hsw>:
ip4_lookup_node_fn_hsw():f010:   55                      push   %rbp

NODE节点宏VLIB_NODE_FN

之后用到的两个宏CLIB_MARCH_SFX和CLIB_MULTIARCH_FN等价。为node处理函数增加架构名称后缀,例如通过指定gcc选项-DCLIB_MARCH_VARIANT=hsw,node处理函数增加hsw后缀。

#ifdef CLIB_MARCH_VARIANT
#define __CLIB_MULTIARCH_FN(a,b) a##_##b
#define _CLIB_MULTIARCH_FN(a,b) __CLIB_MULTIARCH_FN(a,b)
#define CLIB_MULTIARCH_FN(fn) _CLIB_MULTIARCH_FN(fn,CLIB_MARCH_VARIANT)
#else
#define CLIB_MULTIARCH_FN(fn) fn
#endif#define CLIB_MARCH_SFX CLIB_MULTIARCH_FN

节点函数定义宏VLIB_NODE_FN,node节点函数名称首先增加的是_fn字符的后缀,其次是以上的架构后缀(hsw)。__clib_constructor函数为编译器修饰符,指明此为构造函数,其在main函数之前运行。

对于hsw架构,定义了vlib_node_fn_registration_t结构: ( n o d e n a m e ) h s w 。定义了构造函数: (node name)_hsw。定义了构造函数: (nodename)hsw。定义了构造函数:(node name)_multiarch_register_hsw,注册到全局链表。

#define VLIB_NODE_FN(node)                                                         \uword CLIB_MARCH_SFX (node##_fn) ();                                             \static vlib_node_fn_registration_t CLIB_MARCH_SFX (node##_fn_registration) = {   \.function = &CLIB_MARCH_SFX (node##_fn),                                       \};                                                                               \static void __clib_constructor CLIB_MARCH_SFX (node##_multiarch_register) (void) \{                                                                                \extern vlib_node_registration_t node;                                          \vlib_node_fn_registration_t *r;                                                \r = &CLIB_MARCH_SFX (node##_fn_registration);                                  \r->march_variant = CLIB_MARCH_SFX (CLIB_MARCH_VARIANT_TYPE);                   \r->next_registration = node.node_fn_registrations;                             \node.node_fn_registrations = r;                                                \}                                                                                \uword CLIB_MARCH_SFX (node##_fn)

对于x86_64平台,其中march_variant可能得取值有以下几个:

#if defined(__x86_64__)
#define foreach_march_variant                                                 \_ (hsw, "Intel Haswell")                                                    \_ (trm, "Intel Tremont")                                                    \_ (skx, "Intel Skylake (server) / Cascade Lake")                            \_ (icl, "Intel Ice Lake")
#else
#define foreach_march_variant
#endiftypedef enum
{CLIB_MARCH_VARIANT_TYPE = 0,
#define _(s, n) CLIB_MARCH_VARIANT_TYPE_##s,foreach_march_variant
#undef _CLIB_MARCH_TYPE_N_VARIANTS
} clib_march_variant_type_t;

选择节点架构函数

函数vlib_node_get_preferred_node_fn_variant选择优先级最高的架构函数,priority值越大优先级越高。

vlib_node_function_t *
vlib_node_get_preferred_node_fn_variant (vlib_main_t *vm, vlib_node_fn_registration_t *regs)
{vlib_node_main_t *nm = &vm->node_main;vlib_node_fn_registration_t *r;vlib_node_fn_variant_t *v;vlib_node_function_t *fn = 0;...r = regs;while (r) {v = vec_elt_at_index (nm->variants, r->march_variant);if (v->priority > priority) {priority = v->priority;fn = r->function;}r = r->next_registration;}

不同架构对应的优先级如下:

static inline int clib_cpu_march_priority_icl ()
{if (clib_cpu_supports_avx512_bitalg ())return 200;return -1;
}
static inline int clib_cpu_march_priority_skx ()
{if (clib_cpu_supports_avx512f ())return 100;return -1;
}static inline int clib_cpu_march_priority_hsw ()
{if (clib_cpu_supports_avx2 ())return 50;return -1;
}

CLIB_MARCH_FN宏

与以上VLIB_NODE_FN类似,使用CLIB_MARCH_SFX定义多架构函数。不同之处在于使用CLIB_MARCH_FN_SELECT来选择运行时使用的函数。

#define CLIB_MARCH_FN(fn, rtype, _args...)                                    \static rtype CLIB_MARCH_SFX (fn##_ma) (_args);                              \extern rtype (*fn##_selected) (_args);                                      \extern int fn##_selected_priority;                                          \CLIB_MARCH_FN_CONSTRUCTOR (fn)                                              \static rtype CLIB_MARCH_SFX (fn##_ma) (_args)#define CLIB_MARCH_FN_SELECT(fn) (* fn ## _selected)

如下fn ## _selected为最终选择的函数,fn ## _selected_priority为优先级数值。每个注册的架构都执行优先级判断,选出最优的函数。

#define CLIB_MARCH_FN_CONSTRUCTOR(fn)                   \
static void __clib_constructor                      \
CLIB_MARCH_SFX(fn ## _march_constructor) (void)             \
{                                   \if (CLIB_MARCH_FN_PRIORITY() > fn ## _selected_priority)      \{                                   \fn ## _selected = & CLIB_MARCH_SFX (fn ## _ma);           \fn ## _selected_priority = CLIB_MARCH_FN_PRIORITY();      \}                                   \
}                                   \

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/62648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法2--滑动窗口

滑动窗口 滑动窗口经典例题长度最小的子数组无重复字符的最长子串[最大连续1的个数 III](https://leetcode.cn/problems/max-consecutive-ones-iii/description/)[将 x 减到 0 的最小操作数](https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/description…

mysql--二进制安装编译安装yum安装

二进制安装 创建用户和组 [rootlocalhost ~]# groupadd -r -g 306 mysql [rootlocalhost ~]# useradd -r -g 306 -u 306 -d /data/mysql mysql 创建文件夹并添加所属文件用户和组 [rootlocalhost ~]# mkdir -p /data/mysql [rootlocalhost ~]# chown mysql:mysql /data/mysql …

大模型开发和微调工具Llama-Factory-->WebUI

WebUI LLaMA-Factory 支持通过 WebUI 零代码微调大模型。 通过如下指令进入 WebUI llamafactory-cli webui# 如果是国内&#xff0c; # USE_MODELSCOPE_HUB 设为 1&#xff0c;表示模型从 ModelScope 魔搭社区下载。 # 避免从 HuggingFace 下载模型导致网速不畅 USE_MODELSC…

【WPS】【EXCEL】将单元格中字符按照分隔符拆分按行填充到其他单元格

问题&#xff1a;实现如下图的效果 解答&#xff1a; 一、函数 IFERROR(TRIM(MID(SUBSTITUTE($A$2,",",REPT(" ",LEN($A$2))),(ROW(A1)-1)*LEN($A$2)1,LEN($A$2))),"") 二、在单元格C2中填写如下函数 三、全选要填充的单元格并且按CTRLD 函数…

Java有关数组的相关问题

Java中的栈和堆的含义 栈 存储局部变量&#xff1a;栈主要用于存储方法中的局部变量&#xff0c;包括基本数据类型&#xff08;int、double、boolean等&#xff09;和对象的引用&#xff08;不包含对象本身&#xff09;。 遵循后进先出原则&#xff1a;当一个方法被调用时&…

提升阅读体验,Balabolka让文字跃然“声”上

作为一个专业的语音合成工具&#xff0c;Balabolka为用户提供了全方位的文本朗读解决方案。这款软件不仅可以将各类文本实时转换为清晰的语音输出&#xff0c;还能将转换后的音频内容导出为多种主流格式。它强大的兼容性使其能够处理各类电子书和文档格式&#xff0c;让用户可以…

解决`-bash: ./configure:/bin/sh^M:解释器错误: 没有那个文件或目录`的问题

解决`-bash: ./configure:/bin/sh^M:解释器错误: 没有那个文件或目录`的问题 一、错误原因分析二、解决方法方法一:使用`dos2unix`工具方法二:使用`sed`命令方法三:使用`tr`命令方法四:在文本编辑器中转换方法五:在Windows系统中使用适当的工具三、预防措施四、总结在使…

第一部分:基础知识 1. 介绍 --[MySQL轻松入门教程]

什么是MySQL&#xff1f; MySQL 是一种广泛使用的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它使用结构化查询语言&#xff08;SQL&#xff09;来管理数据。MySQL 由瑞典公司 MySQL AB 开发&#xff0c;现在归 Oracle Corporation 所有。MySQL 是开源软件…

Flink双流Join

在离线 Hive 中&#xff0c;我们经常会使用 Join 进行多表关联。那么在实时中我们应该如何实现两条流的 Join 呢&#xff1f;Flink DataStream API 为我们提供了3个算子来实现双流 join&#xff0c;分别是&#xff1a; join coGroup intervalJoin 下面我们分别详细看一下这…

C# 匿名类型和匿名方法

在C#中&#xff0c;匿名类型和匿名方法是两个非常有用的特性&#xff0c;它们可以帮助我们更方便地处理数据和简化代码的编写。 一、匿名类型 (Anonymous Types) 匿名类型允许你在不显式定义类的情况下创建对象。编译器会自动为你生成一个类&#xff0c;其属性由你在对象初始…

【真正离线安装】Adobe Flash Player 32.0.0.156 插件离线安装包下载(无需联网安装)

网上很多人声称并提供的flash离线安装包是需要联网才能安装成功的&#xff0c;其实就是在线安装包&#xff0c;而这里提供的是真正的离线安装包&#xff0c;无需联网即可安装成功。 点击下面地址下载离线安装包&#xff1a; Adobe Flash Player 32.0.0.156 for IE Adobe Fla…

《Vue 组件化开发:构建可复用的模块》

一、Vue 组件化开发概述 组件化是 Vue.js 的核心概念之一&#xff0c;它允许将界面拆分成独立、可复用的组件&#xff0c;使得开发大型应用变得更加简单和高效。 组件的定义是实现应用中局部功能代码和资源的集合。Vue.js 的组件化用于将 UI 页面分割为若干组件进行组合和嵌套…

Python生日祝福烟花

1. 实现效果 2. 素材加载 2个图片和3个音频 shoot_image pygame.image.load(shoot(已去底).jpg) # 加载拼接的发射图像 flower_image pygame.image.load(flower.jpg) # 加载拼接的烟花图 烟花不好去底 # 调整图像的像素为原图的1/2 因为图像相对于界面来说有些大 shoo…

26页PDF | 数据中台能力框架及评估体系解读(限免下载)

一、前言 这份报告详细解读了数据中台的发展历程、核心概念、能力框架及成熟度评估体系。它从阿里巴巴的“大中台&#xff0c;小前台”战略出发&#xff0c;探讨了数据中台如何通过整合企业内部的数据资源和能力&#xff0c;加速业务迭代、降低成本&#xff0c;并推动业务增长…

C++中输入 不定长数据的方法

在未知 所需要输入数据的长度时 &#xff0c;可以用whilegetchar的方法。 char arr1[60] ;int i 0;char c ;while ((c getchar())! \n && c! EOF){arr1[i] c;i;}arr1[i]\0; 这里正常输入字符都是没问题的&#xff0c;while里面的内容都可以正常执行&#xff0c; 当…

Linux系统硬件老化测试脚本:自动化负载与监控

简介&#xff1a; 这篇文章介绍了一款用于Linux系统的自动化硬件老化测试脚本。该脚本能够通过对CPU、内存、硬盘和GPU进行高强度负载测试&#xff0c;持续运行设定的时长&#xff08;如1小时&#xff09;&#xff0c;以模拟长时间高负荷运行的环境&#xff0c;从而验证硬件的稳…

如何分段存储Redis键值对

说明&#xff1a;本文介绍针对一个value过长的键值对&#xff0c;如何分段存储&#xff1b; 场景 当我们需要存入一个String类型的键值对到Redis中&#xff0c;如下&#xff1a; &#xff08;缓存接口&#xff09; public interface CacheService {/*** 添加一个字符串键值…

C-操作符

操作符种类 在C语言中&#xff0c;操作符有以下几种&#xff1a; 算术操作符 移位操作符 位操作符 逻辑操作符 条件操作符 逗号表达式 下标引用&#xff0c;函数调用 拓展&#xff1a;整型提升 我们介绍常用的几个 算术操作符 &#xff08;加&#xff09;&#xff…

RabbitMQ 客户端 连接、发送、接收处理消息

RabbitMQ 客户端 连接、发送、接收处理消息 一. RabbitMQ 的机制跟 Tcp、Udp、Http 这种还不太一样 RabbitMQ 服务&#xff0c;不是像其他服务器一样&#xff0c;负责逻辑处理&#xff0c;然后转发给客户端 而是所有客户端想要向 RabbitMQ服务发送消息&#xff0c; 第一步&a…

浅析大数据时代下的网络安全

一、大数据时代下网络安全的现状 在全球化进程不断深入发展的情况下&#xff0c;互联网行业发展速度也更加迅猛&#xff0c;人们对网络信息的需求量不断增加&#xff0c;所以目前已经进入了大数据时代。 随着计算机技术的不断发展&#xff0c;我国互联网网络规模、网民数量、…