Приложение: То, что вы делаете, на самом деле является продуктом Matrix-Vector.Хорошо понятно, как реализовать это эффективно (хотя с кешированием и параллелизмом на уровне команд это не совсем тривиально).Оставшаяся часть ответа просто показывает очень простую векторизованную реализацию.
Вы можете существенно упростить свою реализацию, увеличив значения n+=8
и i+=1
(предполагая, что out_neurons
кратно 8, в противном случае,необходимо выполнить некоторую специальную обработку для последних элементов), т. е. вы можете накапливать 8 dst
значений одновременно.
Очень простая реализация, предполагающая наличие FMA (в противном случае используйте умножение и сложение):
void dot_product(const float* src, const float* scl, float* dst,
const int in_neurons, const int out_neurons)
{
for(size_t n = 0; n < out_neurons; n+=8){
__m256 accum = _mm256_setzero_ps();
for(size_t i = 0; i < in_neurons; i++){
accum = _mm256_fmadd_ps(_mm256_loadu_ps(&scl[i*out_neurons+n]), _mm256_set1_ps(src[i]), accum);
}
// save the result
_mm256_storeu_ps(dst+n ,accum);
}
}
Это все еще можно оптимизировать, например, путем накопления 2, 4 или 8 dst
пакетов во внутреннем цикле, что не только сохранит некоторые широковещательные операции (инструкция _mm256_set1_ps
), но также компенсируетзадержки инструкции FMA.
Godbolt-Link, если вы хотите поиграться с кодом: https://godbolt.org/z/mm-YHi