多线程
作者:互联网
多线程优化的收获
编译器的优化
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