У меня есть другое приятное решение!
Сначала я хотел бы упомянуть некоторые математические формулы:
averageX = Sum(x[i])/size;
averageY = Sum(y[i])/size;
И поэтому:
Sum((x[i] - averageX)*(y[i] - averageY))/size =
Sum(x[i]*y[i])/size - Sum(x[i]*averageY)/size -
Sum(averageX*y[i])/size + Sum(averageX*averageY)/size =
Sum(x[i]*y[i])/size - averageY*Sum(x[i])/size -
averageX*Sum(y[i])/size + averageX*averageY*Sum(1)/size =
Sum(x[i]*y[i])/size - averageY*averageX -
averageX*averageY + averageX*averageY =
Sum(x[i]*y[i])/size - averageY*averageX;
Это позволяет модифицировать наш алгоритм:
float SigmaXY(const uint8_t * x, const uint8_t * y, size_t size, float averageX, float averageY)
{
uint32_t sum = 0; // If images will have size greater then 256x256 than you have to use uint64_t.
for(size_t i = 0; i < size; ++i)
sum += x[i]*y[i];
return sum/size - averageY*averageX;
}
И только после этого мы может использовать SIMD (я использовал SSE2):
#include <emmintrin.h>
inline __m128i SigmaXY(__m128i x, __m128i y)
{
__m128i lo = _mm_madd_epi16(_mm_unpacklo_epi8(x, _mm_setzero_si128()), _mm_unpacklo_epi8(y, _mm_setzero_si128()));
__m128i hi = _mm_madd_epi16(_mm_unpackhi_epi8(y, _mm_setzero_si128()), _mm_unpackhi_epi8(y, _mm_setzero_si128()));
return _mm_add_epi32(lo, hi);
}
float SigmaXY(const uint8_t * x, const uint8_t * y, size_t size, float averageX, float averageY)
{
uint32_t sum = 0;
size_t i = 0, alignedSize = size/16*16;
if(size >= 16)
{
__m128i sums = _mm_setzero_si128();
for(; i < alignedSize; i += 16)
{
__m128i _x = _mm_loadu_si128((__m128i*)(x + i));
__m128i _y = _mm_loadu_si128((__m128i*)(y + i));
sums = _mm_add_epi32(sums, SigmaXY(_x, _y));
}
uint32_t _sums[4];
_mm_storeu_si128(_sums, sums);
sum = _sums[0] + _sums[1] + _sums[2] + _sums[3];
}
for(; i < size; ++i)
sum += x[i]*y[i];
return sum/size - averageY*averageX;
}
Вы позволили вашему компилятору агрессивно оптимизировать (например, '-funsafe-math-optimizations' или' -Ofast' на gcc)? Если вы этого не сделаете, компилятор не сможет многое сделать с кодом, поскольку он не может векторизовать из-за того, что математика с плавающей запятой не является ассоциативной. – EOF
Отличная точка: с '-O3 -ffast-math -march = haswell', [clang автоматически векторизовывает скалярный код OP так же, как более ранний ответ Ermlg] (http://goo.gl/Db39jB). Он использует нагрузки pmovzx и FMA и использует AVX2 для использования векторов 256b. У этой ссылки godbolt есть некоторые улучшения, над которыми я работаю, как более эффективная горизонтальная сумма. –