我们是否需要在 C++ 中进行矢量化或者 for 循环已经足够快了?
Do we need vectorization in C++ or are for loops already fast enough?
在 Matlab 中我们使用 vectorization 来加速代码。例如,这里有两种执行相同计算的方法:
% Loop
tic
i = 0;
for t = 0:.01:1e5
i = i + 1;
y(i) = sin(t);
end
toc
% Vectorization
tic
t = 0:.01:1e5;
y = sin(t);
toc
结果是:
Elapsed time is 1.278207 seconds. % For loop
Elapsed time is 0.099234 seconds. % Vectorization
所以矢量化代码快了将近 13 倍。实际上,如果我们再次 运行 我们得到:
Elapsed time is 0.200800 seconds. % For loop
Elapsed time is 0.103183 seconds. % Vectorization
矢量化代码现在只有原来的 13 倍快了 2 倍。所以看起来我们在代码的第一个 运行 上获得了巨大的加速,但是在未来的 运行 中,加速没有那么大,因为 Matlab 似乎知道 for 循环没有改变并且是为它优化。在任何情况下,矢量化代码仍然是 for 循环代码的两倍。
现在我开始使用 C++,我想知道这种语言的矢量化。我们是否需要对 C++ 中的循环进行矢量化,或者它们已经足够快了?也许编译器会自动将它们向量化?实际上,我不知道 Matlab 类型向量化是否甚至是 C++ 中的一个概念,也许它只是 Matlab 所需要的,因为这是一种解释型语言?您将如何用 C++ 编写上述函数以使其尽可能高效?
Do we need vectorization in C++
不一定总是需要矢量化,但它可以使某些程序更快。
C++ 编译器支持自动矢量化,但如果您需要进行矢量化,那么您可能无法依赖此类优化,因为并非每个循环都可以自动矢量化。
are [loops] already fast enough?
取决于循环、目标 CPU、编译器及其选项,以及至关重要的:它需要多快。
为了在标准 C++ 中实现矢量化,您可以做一些事情:
- 启用执行自动矢量化的编译器优化。 (请参阅您的编译器手册)
- 指定一个目标 CPU,其指令集中有向量操作。 (请参阅您的编译器手册)
- 使用
std::parallel_unsequenced_policy
或 std::unsequenced_policy
的标准算法。
- 确保正在操作的数据与 SIMD 指令充分对齐。您可以使用
alignas
。请参阅目标手册 CPU 以了解您需要的对齐方式。
- 通过使用link时间优化确保优化器知道尽可能多的信息。
- 部分展开你的循环。这样做的限制是你硬编码并行化的数量:
for (int i = 0; i < count; i += 4) {
operation(i + 0);
operation(i + 1);
operation(i + 2);
operation(i + 3);
}
在标准的、可移植的 C++ 之外,还有特定于实现的方法:
- 一些 compilers 提供语言扩展来编写明确的向量化程序。这可以跨不同的 CPU 移植,但不能移植到未实现扩展的编译器。
using v4si = int __attribute__ ((vector_size (16)));
v4si a, b, c;
a = b + 1; /* a = b + {1,1,1,1}; */
a = 2 * b; /* a = {2,2,2,2} * b; */
在 Matlab 中我们使用 vectorization 来加速代码。例如,这里有两种执行相同计算的方法:
% Loop
tic
i = 0;
for t = 0:.01:1e5
i = i + 1;
y(i) = sin(t);
end
toc
% Vectorization
tic
t = 0:.01:1e5;
y = sin(t);
toc
结果是:
Elapsed time is 1.278207 seconds. % For loop
Elapsed time is 0.099234 seconds. % Vectorization
所以矢量化代码快了将近 13 倍。实际上,如果我们再次 运行 我们得到:
Elapsed time is 0.200800 seconds. % For loop
Elapsed time is 0.103183 seconds. % Vectorization
矢量化代码现在只有原来的 13 倍快了 2 倍。所以看起来我们在代码的第一个 运行 上获得了巨大的加速,但是在未来的 运行 中,加速没有那么大,因为 Matlab 似乎知道 for 循环没有改变并且是为它优化。在任何情况下,矢量化代码仍然是 for 循环代码的两倍。
现在我开始使用 C++,我想知道这种语言的矢量化。我们是否需要对 C++ 中的循环进行矢量化,或者它们已经足够快了?也许编译器会自动将它们向量化?实际上,我不知道 Matlab 类型向量化是否甚至是 C++ 中的一个概念,也许它只是 Matlab 所需要的,因为这是一种解释型语言?您将如何用 C++ 编写上述函数以使其尽可能高效?
Do we need vectorization in C++
不一定总是需要矢量化,但它可以使某些程序更快。
C++ 编译器支持自动矢量化,但如果您需要进行矢量化,那么您可能无法依赖此类优化,因为并非每个循环都可以自动矢量化。
are [loops] already fast enough?
取决于循环、目标 CPU、编译器及其选项,以及至关重要的:它需要多快。
为了在标准 C++ 中实现矢量化,您可以做一些事情:
- 启用执行自动矢量化的编译器优化。 (请参阅您的编译器手册)
- 指定一个目标 CPU,其指令集中有向量操作。 (请参阅您的编译器手册)
- 使用
std::parallel_unsequenced_policy
或std::unsequenced_policy
的标准算法。 - 确保正在操作的数据与 SIMD 指令充分对齐。您可以使用
alignas
。请参阅目标手册 CPU 以了解您需要的对齐方式。 - 通过使用link时间优化确保优化器知道尽可能多的信息。
- 部分展开你的循环。这样做的限制是你硬编码并行化的数量:
for (int i = 0; i < count; i += 4) { operation(i + 0); operation(i + 1); operation(i + 2); operation(i + 3); }
在标准的、可移植的 C++ 之外,还有特定于实现的方法:
- 一些 compilers 提供语言扩展来编写明确的向量化程序。这可以跨不同的 CPU 移植,但不能移植到未实现扩展的编译器。
using v4si = int __attribute__ ((vector_size (16))); v4si a, b, c; a = b + 1; /* a = b + {1,1,1,1}; */ a = 2 * b; /* a = {2,2,2,2} * b; */