其他分享
首页 > 其他分享> > 多线程

多线程

作者:互联网

多线程优化的收获

编译器的优化

gcc/g++的ox优化

主要对编译时间,目标文件长度,执行效率这个三维模型进行不同的取舍和平衡。

优化的方法不一而足,总体上将有以下几类:1)精简操作指令;2)尽量满足cpu的流水操作;3)通过对程序行为地猜测,重新调整代码的执行顺序;4)充分使用寄存器;5)对简单的调用进行展开等等

-O0: 不做任何优化,这是默认的编译选项。
-O1: 对代码的分支,常量,表达式进行优化
-O2:将执行几乎所有的不包含时间和空间折中的优化。与O1比较而言,O2优化增加了编译时间的基础上,提高了生成代码的执行效率。
-O3:在O2的基础上做更多的优化,如内联,针对循环的优化等
-Os:主要是对代码大小的优化

有关矩阵

随机的新尝试

以前


int min,max;
//定义上下边界
 
int range=max-min;
//获取中间的范围
 
int randNum = rand() % range + min;

现在


int min,max;
//定义上下边界
 
default_random_engine e;
//创建引擎
 
uniform_int_distribution<unsigned> u(min,max);
//创建取值范围
 
int randNum=u(e);

为了确保每次调用引擎都是不同的随机数,需要加上static

float* random_matrix(size_t matrix_size){
	static default_random_engine e;
	static uniform_int_distribution<float> u(0.0,10.0);
	float matrix=new float[matrix_size*matrix_size];
	for(size_t i=0;i<matrix_size*matrix_size;i++){
		matrix[i]=u(e);
	}
	return matrix;
}

rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。这就是我们常说的伪随机数。

而定义在 中的随机数库通过一组协作类来解决这类问题:随机数引擎 和 随机数分布类。default_random_engine就是一个随机数引擎。

循环优化

时间复杂度

在使用多线程优化之前不妨来思考一下,矩阵乘本身是否也能进行优化呢,假设两个n*n矩阵相乘,显然矩阵乘法需要做的运算为Aik*Bkj这里k遍历n次,时间复杂度O(n3),虽然这是多项式的复杂度但是当n增大时n3依然太大,所以能不能降低呢?

不妨用矩阵分块试试。
假设A矩阵分为[a,b,c,d],B矩阵分为[e,f,g,h],相乘得到C矩阵[r,s,t,u],那么r=ae+bg,t=ce+dg,s=af+bh,u=cf+dh,
算算时间复杂度T(n)=8T(n/2)+O(n2)=O(n3)完全没有进步!

从这里我们可以看出来重点在8次的乘法导致时间复杂度没有降低,那能不能降低乘法次数呢,哪怕是变成7也好啊,Horner告诉我们完全可以做到。
设p1=a(f-h),p2=h(a+b),p3=e(c+d),p4=d(g-e),p5=(a+d)(e+h),p6=(b-d)(g+h),p7=(a-c)(e+f)
则r=p5+p4-p2+p6,s=p1+p2,t=p3+p4,u=p5+p1-p3-p7(很神奇吧反正我是不可能想到的)

i k j

cache的空间局部性是老生常谈了,算法优化的思想基本离不开这个,这是因为访存的速度直接影响了算法的效率,好的空间局部性能够很好的利用cache的特点,通过比较跳跃步数轻松得到i->k->j的遍历顺序空间局部性最好。

for (size_t i = 0; i < matrix_size; ++i)
{
	auto index_i = matrix_size * i;
	for (size_t j = 0; j < matrix_size; ++j)
	{
		for (size_t k = 0; k < matrix_size; ++k)
		{
			auto index_k = matrix_size * k;
			matrix_C[index_i + j] += matrix_A[index_i + k] * matrix_B[index_k + j];
		}
	}
}

循环展开

流水线也是我们可以考虑的地方,虽然大多数情况下操作系统可以帮我们进行流水线的优化,但是为了保证正确性,操作系统往往具有保守的特性,所以我们可以通过牺牲代码长度把循环展开,这有助于实现软流水,在数据较多时优化明显。

for (size_t i = 0; i < matrix_size; ++i){
	auto index_i = i * matrix_size;
	for (size_t k = 0; k < matrix_size; ++k){
		auto index_k = k * matrix_size;
		auto atmp = matrix_A[index_i + k];
		for (size_t j = 0; j < matrix_size; j += UNROLL){
			size_t B_index0 = index_k + j;
			matrix_C[index_i + j] += atmp * matrix_B[B_index0];
			matrix_C[index_i + j + 1] += atmp * matrix_B[B_index0 + 1];
			matrix_C[index_i + j + 2] += atmp * matrix_B[B_index0 + 2];
			matrix_C[index_i + j + 3] += atmp * matrix_B[B_index0 + 3];
			matrix_C[index_i + j + 4] += atmp * matrix_B[B_index0 + 4];
			matrix_C[index_i + j + 5] += atmp * matrix_B[B_index0 + 5];
			matrix_C[index_i + j + 6] += atmp * matrix_B[B_index0 + 6];
			matrix_C[index_i + j + 7] += atmp * matrix_B[B_index0 + 7];
		}
	}
}

多线程

以前写多线程复杂得很,所幸c++11为我们提供了thread直接调用即可。至于创建多少个线程由逻辑单元的数量决定,核心思想就是由不同的线程同时计算矩阵乘不同的部分,划分体现在最外层循环上,当join()函数被调用后,调用它的线程会被block,直到线程的执行被完成。

thread t1(mulMulti, 0, div);
thread t2(mulMulti, div, 2 * div);
thread t3(mulMulti, 2 * div, 3 * div);
thread t4(mulMulti, 3 * div, m);
t1.join();
t2.join();
t3.join();
for (size_t i = start; i < end; ++i){
	auto index_i = i * matrix_size;
	for (size_t k = 0; k < matrix_size; ++k){
		auto index_k = k * matrix_size;
		auto atmp = A[index_i + k];
		for (size_t j = 0; j < matrix_size; j += UNROLL){
			size_t B_index0 = index_k + j;
			C[index_i + j] += atmp * B[B_index0];
			C[index_i + j + 1] += atmp * B[B_index0 + 1];
			C[index_i + j + 2] += atmp * B[B_index0 + 2];
			C[index_i + j + 3] += atmp * B[B_index0 + 3];
			C[index_i + j + 4] += atmp * B[B_index0 + 4];
			C[index_i + j + 5] += atmp * B[B_index0 + 5];
			C[index_i + j + 6] += atmp * B[B_index0 + 6];
			C[index_i + j + 7] += atmp * B[B_index0 + 7];
		}
	}
}

标签:index,matrix,atmp,index0,多线程,优化,size
来源: https://blog.csdn.net/weixin_50281113/article/details/115265724