Я бы использовал SSE.
Редактировать: Я сам провел еще несколько тестов и обнаружил, что ваша программа не ограничена ни пропускной способностью памяти (теоретический предел примерно в 3-4 раза выше, чем ваш результат), ниПроизводительность с плавающей запятой (с еще более высоким пределом) ограничивается отложенным выделением страниц памяти операционной системой.
#include <chrono>
#include <iostream>
#include <x86intrin.h>
using namespace std::chrono;
static const unsigned size = 49335264;
float data[size], other_data[size], other_data2[size];
int main() {
#if 0
for(unsigned i=0; i<size; i++) {
data[i] = i;
other_data[i] = i;
other_data2[i] = i;
}
#endif
system_clock::time_point start = system_clock::now();
for(unsigned i=0; i<size; i++)
data[i] += other_data[i]*other_data2[i];
microseconds timeUsed = system_clock::now() - start;
std::cout << "Used " << timeUsed.count() << " us, "
<< 2*size/(timeUsed.count()/1e6*1e9) << " GFLOPS\n";
}
Перевод с g++ -O3 -march=native -std=c++0x
.Программа выдает
Used 212027 us, 0.465368 GFLOPS
в качестве выходных данных, хотя горячий цикл преобразуется в
400848: vmovaps 0xc234100(%rdx),%ymm0
400850: vmulps 0x601180(%rdx),%ymm0,%ymm0
400858: vaddps 0x17e67080(%rdx),%ymm0,%ymm0
400860: vmovaps %ymm0,0x17e67080(%rdx)
400868: add $0x20,%rdx
40086c: cmp $0xbc32f80,%rdx
400873: jne 400848 <main+0x18>
Это означает, что он полностью векторизован, использует 8 операций с плавающей запятой на итерацию и даже использует преимущества AVX.После игры с потоковой инструкцией, такой как movntdq
, которая ничего не купила, я решил на самом деле инициализировать массивы чем-то - иначе они будут нулевыми страницами, которые будут отображаться в реальную память только в том случае, если они записаны.Изменение #if 0
на #if 1
немедленно приводит к
Used 48843 us, 2.02016 GFLOPS
, что очень близко к пропускной способности памяти системы (4 плавают 4 байта на два FLOPS = 16 ГБайт / с, теоретический предел равен 2Каналы DDR3 каждые 10 667 ГБайт / с).