目标
本教程的目标是提供使用通用内部函数功能矢量化 C++ 代码以提高运行时速度的指南。我们将简要介绍 SIMD 内部函数以及如何使用宽寄存器,然后介绍使用宽寄存器的基本操作。
理论
在本节中,我们将简要介绍一些概念,以帮助更好地理解该功能。
内部函数
内部函数是由编译器单独处理的函数。这些函数通常经过优化,以最有效的方式执行,因此运行速度比正常实现更快。但是,由于这些函数依赖于编译器,因此很难编写可移植的应用程序。
SIMD的
SIMD 代表 单指令,多数据。SIMD 内部函数允许处理器对计算进行矢量化。数据存储在所谓的寄存器中。寄存器可以是 128 位、256 位或 512 位宽。每个寄存器存储相同数据类型的多个值。寄存器的大小和每个值的大小决定了总共存储的值的数量。
根据 CPU 支持的指令集,您可以使用不同的寄存器。要了解更多信息,请看这里
通用内部函数
OpenCV 的通用内部函数提供了对 SIMD 矢量化方法的抽象,并允许用户使用内部函数,而无需编写特定于系统的代码。
OpenCV Universal Intrinsics 支持以下指令集:
- 支持各种类型的 128 位寄存器,适用于各种架构,包括
- x86(SSE/SSE2/SSE4.2),
- 手臂(霓虹灯),
- 电源PC(VSX),
- MIPS(MSA)。
- x86(AVX2) 支持 256 位寄存器,并且
- x86(AVX512) 支持 512 位寄存器
现在,我们将介绍可用的结构和功能:
- 寄存器结构
- 加载和存储
- 数学运算
- 减少和遮罩
寄存器结构
通用内部函数集将每个寄存器实现为基于特定 SIMD 寄存器的结构。所有类型都包含枚举,该枚举给出类型可以容纳的值的确切数量。这样就无需在实现过程中对值的数量进行硬编码。nlanes
-
注意
每个寄存器结构都在命名空间下。
cv
有两种类型的寄存器:
-
可变大小的寄存器:这些结构没有固定的大小,它们的确切位长度是在编译过程中根据可用的 SIMD 功能推断出来的。因此,枚举的值是在编译时确定的。
nlanes
每个结构都遵循以下约定:
v_[type of value][size of each value in bits]
例如,v_uint8 保存 8 位无符号整数,v_float32保存 32 位浮点值。然后我们声明一个寄存器,就像我们在 C++ 中声明任何对象一样
根据可用的 SIMD 指令集,特定寄存器将保存不同数量的值。例如:如果您的计算机支持最大 256 位寄存器,
-
v_uint8将保存 32 个 8 位无符号整数
-
v_float64将容纳 4 个 64 位浮点数(双精度)
v_uint8 a; // a is a register supporting uint8(char) data int n = a.nlanes; // n holds 32
可用的数据类型和大小:
类型 大小(以位为单位) uint 8, 16, 32, 64 int 8, 16, 32, 64 浮 32, 64 -
-
恒