【SIMD加速循环操作】

AVX2单指令多数据加速一个小小实验

本来是一个超级超级超级简单的小实验,就是在0~0xFFFFFFFF中暴力从头搜到尾搜出随机数生成函数生成的指定密码。一般来说,就这么直接写一个直白的程序,大概30多秒跑完,小实验要求呢主要是让大家开开多线程,和自己的电脑多核多线程尽量适配。开个多线程嘛,超容易的,把这么多数平均分给每个线程去找就行啦,一下子就写好了,嗯加速也不错,把主频调高之后就很快,6-7秒左右(轻薄本主频不算高啦,核也不多)

然后,就脑子一热,自找苦吃…说要试试SIMD技术…于是就有了这篇文章…


首先是原题和原始搜索代码

/*
	某个应用程序的随机数产生机制是这样的:
	1、首先产生一个0~0xFFFFFFFF之间的数,称为“密码”X
	2、用(X,0x29A)调用随机数更新函数GenerateRandomNumber,产生新的(Y,Z)
	3、用(Y,Z)调用随机数更新函数GenerateRandomNumber,产生新的(A,B)
	4、把B传给用户
	
	本程序(你的)目的,就是在知道这套机制、知道B的前提下,计算出X是多少?
	
	非常简单的思路:
	1、循环,从i=0~0xFFFFFFFF
	2、GenerateRandomNumber(i,0x29A)
	3、GenerateRandomNumber(A,B)
	4、判断B是否等于想要找的值
	
*/

#include 
#include 

/* 更新随机数 */
void GenerateRandomNumber(unsigned int *rand1_h,unsigned int *rand1_l)
{
	/*请忽略下列代码,知道是产生更新随机数即可,原理不必弄清楚*/
	unsigned long long x = (unsigned long long)*rand1_h;
	x *= 0x6AC690C5;
	x += *rand1_l;

	*rand1_h = (unsigned int)x;
	*rand1_l = (unsigned int)(x>>32);
}

void SearchRandomNumber(unsigned int begin_h,unsigned int begin_l, unsigned int search_val)
{
	/* 在begin_l~begin_h之间,计算B的值,看看是否和search_val相等*/
	unsigned int i;
	unsigned int h,l;

	for (i=begin_l;i<begin_h;i++)
	{
		h = i;
		l = 0x29A;
		GenerateRandomNumber(&h,&l);	// 第一次调用
		GenerateRandomNumber(&h,&l);	// 第二次调用
		if (l==search_val)						// 判断B是否和search_val相等
		{
			printf("找到啦~! 密码是%08X\n",i);	// 相等,找到了~~
		}
	}
}

int main()
{
	time_t begin_time,end_time;
	
	printf("这是一个标准、最慢的搜索程序,拼主频!\n");
	
	begin_time = time(NULL);							// 记录开始时刻
	SearchRandomNumber(0xFFFFFFFF,0,0x39A6FFBB);	// 在0~0xFFFFFFFF之间进行搜索
	// 0x39A6FFBB => 0x70FFFFFF
	end_time = time(NULL);								// 记录结束时刻

	printf("耗时%ld秒~~\n",end_time-begin_time);	// 结束-开始=耗时
  return 0;
}

SIMD技术介绍

啊好痛苦,这个技术主要就是在这些库里,分别对应不同的版本。我也分不清具体是哪个,用的时候查一查就好。我这里用到的是AVX2这个类型的,所以后面的内容都是基于AVX2这个技术写的

#include 
#include 
#include 
#include 
#include 
数据类型
__m256i result256//256位的一个整型数据,依据赋值的不同,看作8个32位、4个64位、16个16位等等
常用函数

从一个__m256i的指针指向的地址处加载256位,返回__m256i数据类型

_mm256_loadu_si256((__m256i*)rand1_h)

按照8个32位整型设置每个数的值,第一个参数设置的是下标为7的那个数(高位),以此类推。

_mm256_set_epi32((int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5);

加法和乘法;但是要注意,32位的乘法并不会对8个数进行运算,而是对两个向量中第0,2,4,6个32位整数进行相乘,并扩展到64位(要考虑乘法的溢出)

result256 = _mm256_mul_epu32(cc,load256);
result256 = _mm256_add_epi64(result256,adder256);

大概类似一种“重组”的意思。通过control的每个控制数,选择该索引对应的数是原向量中的哪一个。例如control(__m256i类型)被设置为77553311,则result_l的第6,7号元素都为result256中的第7号元素

__m256i result_l = _mm256_permutevar8x32_epi32(result256,control);

最终实现(重点部分)

/*
	某个应用程序的随机数产生机制是这样的:
	1、首先产生一个0~0xFFFFFFFF之间的数,称为“密码”X
	2、用(X,0x29A)调用随机数更新函数GenerateRandomNumber,产生新的(Y,Z)
	3、用(Y,Z)调用随机数更新函数GenerateRandomNumber,产生新的(A,B)
	4、把B传给用户
	
	本程序(你的)目的,就是在知道这套机制、知道B的前提下,计算出X是多少?
	
	非常简单的思路:
	1、循环,从i=0~0xFFFFFFFF
	2、GenerateRandomNumber(i,0x29A)
	3、GenerateRandomNumber(A,B)
	4、判断B是否等于想要找的值
	
*/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define SHIFT 3

sem_t mutex;
int num=0;


typedef struct mypara
{
	unsigned int begin;
	unsigned int end;
	unsigned int search_val;
}PARA;



/* 更新随机数 */
void GenerateRandomNumber(unsigned int *rand1_h,unsigned int *rand1_l)
{
	/*请忽略下列代码,知道是产生更新随机数即可,原理不必弄清楚*/
        int i=0;
        __m256i result256 = _mm256_setzero_si256();
        __m256i load256 = _mm256_loadu_si256((__m256i*)rand1_h);
        __m256i cc = _mm256_set_epi32((int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5,(int)0x6AC690C5);
        __m256i adder256 = _mm256_loadu_si256((__m256i*)rand1_l);
        result256 = _mm256_mul_epu32(cc,load256);
        result256 = _mm256_add_epi64(result256,adder256);
        
        __m256i control = _mm256_set_epi32((int)7,(int)7,(int)5,(int)5,(int)3,(int)3,(int)1,(int)1);
        
        __m256i result_l = _mm256_permutevar8x32_epi32(result256,control);
        __m256i mask = _mm256_set_epi32((int)0,(int)0xffffffff,(int)0,(int)0xffffffff,(int)0,(int)0xffffffff,(int)0,(int)0xffffffff);
        result_l = _mm256_and_si256(result_l,mask);
        result256 = _mm256_and_si256(result256,mask);
         _mm256_storeu_si256((__m256i*)rand1_h, result256);
         _mm256_storeu_si256((__m256i*)rand1_l, result_l);

}


int GenerateRandomNumber0(unsigned int *rand1_h,unsigned int *rand1_l)
{
	/*请忽略下列代码,知道是产生更新随机数即可,原理不必弄清楚*/
	unsigned long long x = (unsigned long long)*rand1_h;
	x *= 0x6AC690C5;
	x += *rand1_l;

	*rand1_h = (unsigned int)x;
	*rand1_l = (unsigned int)(x>>32);
	return 0;
}



如果要发挥SIMD指令的效果,在编译的时候要开一些编译或者优化选项,具体的每一次我自己都记不得,都是临时查的哈哈哈哈,最终效果还算不错,开一点点优化的话2-3秒可以完成。不开优化的话其实也是6-7秒…估计是在调用那里循环写多了,访存有点多emmmm好吧,但至少成功实现了!程序行为是对的已经很令我满意了

你可能感兴趣的:(c语言)