Ответ 1
Флаг O3
автоматически включает-векторизовать. https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
-O3 включает все оптимизации, заданные -O2, а также включает -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 и -fipa-cp-clone options
Итак, в обоих случаях компилятор пытается сделать векторизацию цикла.
Использование g++ 4.8.2 для компиляции с помощью:
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
Возвращает только это:
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;
}
Как я уже сказал, у меня были проблемы с согласованными измерениями времени, поэтому не могу подтвердить, что это даст вам увеличение производительности (или даже даже уменьшение!)