如何用 g++ 向量化我的循环?
How to vectorize my loop with g++?
搜索到的介绍链接:
- 6.59.14 Loop-Specific Pragmas
- 2.100 Pragma Loop_Optimize
- How to give hint to gcc about loop count
- Tell gcc to specifically unroll a loop
- How to Force Vectorization in C++
如您所见,它们中的大部分都是针对 C 的,但我认为它们也可能适用于 C++。这是我的代码:
template<typename T>
//__attribute__((optimize("unroll-loops")))
//__attribute__ ((pure))
void foo(std::vector<T> &p1, size_t start,
size_t end, const std::vector<T> &p2) {
typename std::vector<T>::const_iterator it2 = p2.begin();
//#pragma simd
//#pragma omp parallel for
//#pragma GCC ivdep Unroll Vector
for (size_t i = start; i < end; ++i, ++it2) {
p1[i] = p1[i] - *it2;
p1[i] += 1;
}
}
int main()
{
size_t n;
double x,y;
n = 12800000;
vector<double> v,u;
for(size_t i=0; i<n; ++i) {
x = i;
y = i - 1;
v.push_back(x);
u.push_back(y);
}
using namespace std::chrono;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
foo(v,0,n,u);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
return 0;
}
我使用了上面评论的所有提示,但我没有得到任何加速,如示例输出所示(第一个 运行 取消注释此 #pragma GCC ivdep Unroll Vector
:
samaras@samaras-A15:~/Downloads$ g++ test.cpp -O3 -std=c++0x -funroll-loops -ftree-vectorize -o test
samaras@samaras-A15:~/Downloads$ ./test
It took me 0.026575 seconds.
samaras@samaras-A15:~/Downloads$ g++ test.cpp -O3 -std=c++0x -o test
samaras@samaras-A15:~/Downloads$ ./test
It took me 0.0252697 seconds.
还有希望吗?或者优化标志 O3
就可以解决问题?欢迎任何加速此代码(foo
函数)的建议!
我的 g++ 版本:
samaras@samaras-A15:~/Downloads$ g++ --version
g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1
注意循环体是随机的。我没有兴趣以其他形式重写它。
编辑
无能为力的回答也可以!
O3
标志自动打开 -ftree-vectorize
。 https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
-O3 turns on all optimizations specified by -O2 and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-loop-vectorize, -ftree-loop-distribute-patterns, -ftree-slp-vectorize, -fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone options
所以在这两种情况下,编译器都在尝试进行循环矢量化。
使用 g++ 4.8.2 编译:
# In newer versions of GCC use -fopt-info-vec-missed instead of -ftree-vectorize
g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorize -ftree-vectorizer-verbose=1 -o test
给出这个:
Analyzing loop at test.cpp:16
Vectorizing loop at test.cpp:16
test.cpp:16: note: create runtime check for data references *it2$_M_current_106 and *_39
test.cpp:16: note: created 1 versioning for alias checks.
test.cpp:16: note: LOOP VECTORIZED.
Analyzing loop at test_old.cpp:29
test.cpp:22: note: vectorized 1 loops in function.
test.cpp:18: note: Unroll loop 7 times
test.cpp:16: note: Unroll loop 7 times
test.cpp:28: note: Unroll loop 1 times
在没有 -ftree-vectorize
标志的情况下编译:
g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorizer-verbose=1 -o test
Returns只有这个:
test_old.cpp:16: note: Unroll loop 7 times
test_old.cpp:28: note: Unroll loop 1 times
第 16 行是循环函数的开始,因此编译器肯定在对其进行矢量化。检查汇编程序也证实了这一点。
我似乎在我目前使用的笔记本电脑上进行了一些激进的缓存,这使得很难准确测量该功能需要多长时间 运行。
但是您也可以尝试其他一些事情:
使用__restrict__
限定符告诉编译器数组之间没有重叠。
告诉编译器数组与 __builtin_assume_aligned
对齐(不可移植)
这是我生成的代码(我删除了模板,因为您需要对不同的数据类型使用不同的对齐方式)
#include <iostream>
#include <chrono>
#include <vector>
void foo( double * __restrict__ p1,
double * __restrict__ p2,
size_t start,
size_t end )
{
double* pA1 = static_cast<double*>(__builtin_assume_aligned(p1, 16));
double* pA2 = static_cast<double*>(__builtin_assume_aligned(p2, 16));
for (size_t i = start; i < end; ++i)
{
pA1[i] = pA1[i] - pA2[i];
pA1[i] += 1;
}
}
int main()
{
size_t n;
double x, y;
n = 12800000;
std::vector<double> v,u;
for(size_t i=0; i<n; ++i) {
x = i;
y = i - 1;
v.push_back(x);
u.push_back(y);
}
using namespace std::chrono;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
foo(&v[0], &u[0], 0, n );
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
return 0;
}
就像我说的,我很难获得一致的时间测量值,所以无法确认这是否会给您带来性能提升(或者甚至可能降低!)
GCC 对编译器进行了扩展,可以创建将使用 SIMD 指令的新原语。查看here了解详情。
大多数编译器说他们会自动矢量化操作,但这取决于编译器模式匹配,但正如您想象的那样,这可能会很偶然。
搜索到的介绍链接:
- 6.59.14 Loop-Specific Pragmas
- 2.100 Pragma Loop_Optimize
- How to give hint to gcc about loop count
- Tell gcc to specifically unroll a loop
- How to Force Vectorization in C++
如您所见,它们中的大部分都是针对 C 的,但我认为它们也可能适用于 C++。这是我的代码:
template<typename T>
//__attribute__((optimize("unroll-loops")))
//__attribute__ ((pure))
void foo(std::vector<T> &p1, size_t start,
size_t end, const std::vector<T> &p2) {
typename std::vector<T>::const_iterator it2 = p2.begin();
//#pragma simd
//#pragma omp parallel for
//#pragma GCC ivdep Unroll Vector
for (size_t i = start; i < end; ++i, ++it2) {
p1[i] = p1[i] - *it2;
p1[i] += 1;
}
}
int main()
{
size_t n;
double x,y;
n = 12800000;
vector<double> v,u;
for(size_t i=0; i<n; ++i) {
x = i;
y = i - 1;
v.push_back(x);
u.push_back(y);
}
using namespace std::chrono;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
foo(v,0,n,u);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
return 0;
}
我使用了上面评论的所有提示,但我没有得到任何加速,如示例输出所示(第一个 运行 取消注释此 #pragma GCC ivdep Unroll Vector
:
samaras@samaras-A15:~/Downloads$ g++ test.cpp -O3 -std=c++0x -funroll-loops -ftree-vectorize -o test
samaras@samaras-A15:~/Downloads$ ./test
It took me 0.026575 seconds.
samaras@samaras-A15:~/Downloads$ g++ test.cpp -O3 -std=c++0x -o test
samaras@samaras-A15:~/Downloads$ ./test
It took me 0.0252697 seconds.
还有希望吗?或者优化标志 O3
就可以解决问题?欢迎任何加速此代码(foo
函数)的建议!
我的 g++ 版本:
samaras@samaras-A15:~/Downloads$ g++ --version
g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1
注意循环体是随机的。我没有兴趣以其他形式重写它。
编辑
无能为力的回答也可以!
O3
标志自动打开 -ftree-vectorize
。 https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
-O3 turns on all optimizations specified by -O2 and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-loop-vectorize, -ftree-loop-distribute-patterns, -ftree-slp-vectorize, -fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone options
所以在这两种情况下,编译器都在尝试进行循环矢量化。
使用 g++ 4.8.2 编译:
# In newer versions of GCC use -fopt-info-vec-missed instead of -ftree-vectorize
g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorize -ftree-vectorizer-verbose=1 -o test
给出这个:
Analyzing loop at test.cpp:16
Vectorizing loop at test.cpp:16
test.cpp:16: note: create runtime check for data references *it2$_M_current_106 and *_39
test.cpp:16: note: created 1 versioning for alias checks.
test.cpp:16: note: LOOP VECTORIZED.
Analyzing loop at test_old.cpp:29
test.cpp:22: note: vectorized 1 loops in function.
test.cpp:18: note: Unroll loop 7 times
test.cpp:16: note: Unroll loop 7 times
test.cpp:28: note: Unroll loop 1 times
在没有 -ftree-vectorize
标志的情况下编译:
g++ test.cpp -O2 -std=c++0x -funroll-loops -ftree-vectorizer-verbose=1 -o test
Returns只有这个:
test_old.cpp:16: note: Unroll loop 7 times
test_old.cpp:28: note: Unroll loop 1 times
第 16 行是循环函数的开始,因此编译器肯定在对其进行矢量化。检查汇编程序也证实了这一点。
我似乎在我目前使用的笔记本电脑上进行了一些激进的缓存,这使得很难准确测量该功能需要多长时间 运行。
但是您也可以尝试其他一些事情:
使用
__restrict__
限定符告诉编译器数组之间没有重叠。告诉编译器数组与
__builtin_assume_aligned
对齐(不可移植)
这是我生成的代码(我删除了模板,因为您需要对不同的数据类型使用不同的对齐方式)
#include <iostream>
#include <chrono>
#include <vector>
void foo( double * __restrict__ p1,
double * __restrict__ p2,
size_t start,
size_t end )
{
double* pA1 = static_cast<double*>(__builtin_assume_aligned(p1, 16));
double* pA2 = static_cast<double*>(__builtin_assume_aligned(p2, 16));
for (size_t i = start; i < end; ++i)
{
pA1[i] = pA1[i] - pA2[i];
pA1[i] += 1;
}
}
int main()
{
size_t n;
double x, y;
n = 12800000;
std::vector<double> v,u;
for(size_t i=0; i<n; ++i) {
x = i;
y = i - 1;
v.push_back(x);
u.push_back(y);
}
using namespace std::chrono;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
foo(&v[0], &u[0], 0, n );
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
return 0;
}
就像我说的,我很难获得一致的时间测量值,所以无法确认这是否会给您带来性能提升(或者甚至可能降低!)
GCC 对编译器进行了扩展,可以创建将使用 SIMD 指令的新原语。查看here了解详情。
大多数编译器说他们会自动矢量化操作,但这取决于编译器模式匹配,但正如您想象的那样,这可能会很偶然。