问题描述
我正在尝试了解内在以及如何正确利用和优化它,我决定实现一个函数来获得两个数组的点积作为学习的起点。
我创建了两个函数来获取整数数组 int
的点积,一个以正常方式编码,您循环遍历两个数组的每个元素,然后对每个元素执行乘法,然后添加/累加/sum 得到的乘积得到点积。
另一个使用内在的方式,我对每个数组的四个元素执行内在操作,我使用 _mm_mullo_epi32
将它们中的每一个相乘,然后使用 2 水平相加 {{1 }} 获取当前 4 个元素的总和,然后将其加到 dot_product,然后继续下一个 4 个元素,然后重复直到达到计算的限制 _mm_hadd_epi32
,然后我计算其他剩余元素使用正常方式避免计算出数组的内存,然后我比较两者的性能。
vec_loop
// main.hpp
#ifndef main_hpp
#define main_hpp
#include <iostream>
#include <immintrin.h>
template<typename T>
T scalar_dot(T* a,T* b,size_t len){
T dot_product = 0;
for(size_t i=0; i<len; ++i) dot_product += a[i]*b[i];
return dot_product;
}
int sse_int_dot(int* a,int* b,size_t len){
size_t vec_loop = len/4;
size_t non_vec = len%4;
size_t start_non_vec_i = len-non_vec;
int dot_prod = 0;
for(size_t i=0; i<vec_loop; ++i)
{
__m128i va = _mm_loadu_si128((__m128i*)(a+(i*4)));
__m128i vb = _mm_loadu_si128((__m128i*)(b+(i*4)));
va = _mm_mullo_epi32(va,vb);
va = _mm_hadd_epi32(va,va);
va = _mm_hadd_epi32(va,va);
dot_prod += _mm_cvtsi128_si32(va);
}
for(size_t i=start_non_vec_i; i<len; ++i) dot_prod += a[i]*b[i];
return dot_prod;
}
#endif
编译:
- 内在版本:
// main.cpp #include <iostream> #include <chrono> #include <random> #include "main.hpp" int main() { // generate random integers unsigned seed = std::chrono::steady_clock::Now().time_since_epoch().count(); std::mt19937_64 rand_engine(seed); std::mt19937_64 rand_engine2(seed/2); std::uniform_int_distribution<int> random_number(0,9); size_t LEN = 10000000; int* a = new int[LEN]; int* b = new int[LEN]; for(size_t i=0; i<LEN; ++i) { a[i] = random_number(rand_engine); b[i] = random_number(rand_engine2); } #ifdef SCALAR int dot1 = 0; #endif #ifdef VECTOR int dot2 = 0; #endif // timing auto start = std::chrono::high_resolution_clock::Now(); #ifdef SCALAR dot1 = scalar_dot(a,b,LEN); #endif #ifdef VECTOR dot2 = sse_int_dot(a,LEN); #endif auto end = std::chrono::high_resolution_clock::Now(); auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end-start); std::cout<<"proccess taken "<<duration.count()<<" nanoseconds\n"; #ifdef SCALAR std::cout<<"\nScalar : Dot product = "<<dot1<<"\n"; #endif #ifdef VECTOR std::cout<<"\nVector : Dot product = "<<dot2<<"\n"; #endif return 0; }
- 普通版:
g++ main.cpp -DVECTOR -msse4.1 -o main.o
我的机器:
- 架构:x86_64
- cpu:1
- cpu 内核:4
- 每核线程数:1
- 型号名称:Intel(R) Pentium(R) cpu N3700 @ 1.60GHz
- 一级缓存:96 KiB
- L1i 缓存:128 KiB
- 二级缓存:2 MiB
- 一些标志:sse、sse2、sse4_1、sse4_2
在g++ main.cpp -DSCALAR -msse4.1 -o main.o
中有main.cpp
数组的10000000个元素,当我在我的机器上编译上面的代码时,似乎内部函数运行速度比普通版本慢,大多数时候,内部函数需要int
,有时甚至更长,而普通代码只需要97529675 nanoseconds
,这里我认为我的内部函数应该运行得更快如果优化标志关闭,但结果确实有点慢。
所以我的问题是:
希望有人能帮忙,谢谢
解决方法
因此,根据@Peter Cordes、@Qubit 和@j6t 的建议,我稍微调整了代码,现在只在循环内进行乘法运算,然后将水平加法移到循环外。 .. 它设法将内在版本的性能从 Thread
左右提高到 97529675 nanoseconds
左右,这比我之前的实现要快得多,编译标志相同,10000000 int 数组的元素。
这里是 main.hpp 中的新函数
56444187 nanoseconds
如果此代码还有更多需要改进的地方,请指出,现在我将把它留在这里作为答案。