`
eyeyin
  • 浏览: 4113 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

基于SSE的多线程矩阵乘积算法

阅读更多
/*
 ============================================================================
 Name        : matrix_mul_block.c
 Author      : yin
 Version     :
 Copyright   : Copyright received yinhongliang
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <pthread.h>
#include <time.h>
#include <math.h>
#include <xmmintrin.h>

#define CLK_TCK 150000 //由主频决定的每个clock周期的倒数
#define L1_SIZE 32768  //L1 Cache的大小
#define CACHE_LINE_SIZE 64 //cache调度的行所占字节数
float *matrix_a, *matrix_b, *matrix_c1, *matrix_c2;
int n;//矩阵大小,每行元素个数
int thread_num;//线程个数
float L1_rate = 1;//分组占用L1空间比率
int global_index = 0;
pthread_mutex_t mutex1;
int local_index = 0;
FILE *fp;

// @param n int 矩阵大小
// @return void
// 两个矩阵的初始化
void init(int n) {
	int i = 0, j = 0;
	char* filename = "log.txt";
	clock_t start, end;
	start=clock();
	if ((fp = fopen(filename, "a+")) == NULL) {
		printf("cannot open this file %s in blocked_matrix_mul.\n ", filename);
		exit(0);
	}
	if ((matrix_a = (float *) malloc(n * n * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for matrix_a\n");
		exit(1);
	}
	if ((matrix_b = (float *) malloc(n * n * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for matrix_b\n");
		exit(1);
	}
	if ((matrix_c1 = (float *) malloc(n * n * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for matrix_c1\n");
		exit(1);
	}
	if ((matrix_c2 = (float *) malloc(n * n * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for matrix_c2\n");
		exit(1);
	}
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			*(matrix_a + n * i + j) = (rand() % 10) / 10.0;
		}
	}
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			*(matrix_b + n * i + j) = (rand() % 10) / 10.0;
		}
	}
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			*(matrix_c1 + n * i + j) = 0;
			*(matrix_c2 + n * i + j) = 0;
		}
	}
}

// @param thread_num	int 线程数目
// @param n 			int 矩阵大小
// @param L1_rate	 	float 占用L1比率,1则为全部占用,0.5则为占用一半
// @return int
int main(int argc, char* args[]) {
	clock_t start, end;//分别记录函数执行前后的时钟数
	float cost;//函数执行时间
	if (argc != 4) {
		thread_num = 1;
		n = 500;
		L1_rate = 1.0;
	} else if (argc == 4) {
		thread_num = atoi(args[1]);
		n = atoi(args[2]);
		L1_rate = atof(args[3]);
	}
	init(n);

	//	串行程序
	start = clock();
	serial(n);
	end = clock();
	cost = (float) (end - start) / CLK_TCK;
	printf("The serial program cost time:T=%fs\n", cost);

	//	并行程序
	start = clock();
	parallel(thread_num, n, L1_rate);
	end = clock();
	cost = (float) (end - start) / CLK_TCK;
	printf("The parallel program cost time:T=%fs\n", cost);
	compare();
	uninit();
	return 0;
}
//@param n 矩阵长度
//@return void
//@discribe 串行计算两个大小为n的矩阵的乘积
void serial(int n) {
	int i, j, k;
	float temp = 0.0;
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			temp = 0;
			for (k = 0; k < n; k++) {
				//在结果矩阵中,逻辑坐标为(i,j)的元素
				//是逻辑坐标为(i,k)和(k,j)的元素乘积之和
				temp += matrix_a[i * n + k] * matrix_b[k * n + j];
			}
			matrix_c1[i * n + j] = temp;
		}
	}
}
//@param matrix_left int* 乘法公式左边矩阵分块
//@param row_left int     乘法公式左边矩阵分块行数
//@param col_left int     乘法公式左边矩阵分块列数
//@param matrix_right int* 乘法公式右边矩阵分块
//@param row_right int     乘法公式右边矩阵分块行数
//@param col_right int     乘法公式右边矩阵分块列数
//@param matrix_res int*   结果矩阵分块
//@return void
//@describe 两个矩阵分块相乘
void blocked_matrix_mul(float* matrix_left, int row_left, int col_left,
		float* matrix_right, int row_right, int col_right, float * matrix_res) {
	int i, j, k;
	float temp;
	float* array_left;
	float* array_right;
	float* array_res;

	if ((array_left = (float *) malloc(col_left * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for array_left\n");
		exit(1);
	}
	if ((array_right = (float *) malloc(col_left * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for array_right\n");
		exit(1);
	}
	if ((array_res = (float *) malloc(col_left * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for array_res\n");
		exit(1);
	}

	//两个矩阵相乘,必须要求左边矩阵的列数与右边矩阵的行数相等
	if (col_left != row_right) {
		printf("Illegal matrixes multiply in blocked_matrix_mul.\n");
		return;
	}

	//串行计算两个矩阵的乘积
	for (i = 0; i < row_left; i++) {
		for (j = 0; j < col_right; j++) {
//			fprintf(fp,"Compute array_res[%d][%d].\n",i,j);
			for (k = 0; k < col_left; k++) {
				//对应相乘的元素分别是matrix_left(i,k),即matrix_left[i*col_left+k],
				//matrix_right(k,j),即matrix_right[k*col_right+j]
				array_left[k] = matrix_left[i * col_left + k];
				array_right[k] = matrix_right[k * col_right + j];
			}
			temp = 0;
			ComputeArraySSE(array_left, array_right, array_res, col_left);
			for (k = 0; k < col_left; k++) {
				temp += array_res[k];
			}
			matrix_res[i * col_right + j] = temp;
		}
	}
}

void *slave(void *ignored) {
	int i = 0, j = 0, k = 0, l = 0, m = 0;
	int block_row, block_col; //矩阵分块在原矩阵中的位置
	int block_row_count; //矩阵分块的行数
	int block_col_count; //矩阵分块的列数
	int row_left, col_left;//乘法公式左边矩阵分块的行数和列数
	int row_right, col_right;//乘法公式右边矩阵分块的行数和列数
	int row_block_count; //同一列可见的矩阵分块的个数
	int col_block_count; //同一行可见的矩阵分块的个数
	int block_count;//总的矩阵分块的个数
	int block_size; //矩阵分块存储的元素个数
	float *block_left, *block_right, *block_res;

	//每个cache行能存储CACHE_LINE_SIZE/sizeof(int)个数,
	//所以将矩阵分块的列数定为CACHE_LINE_SIZE/sizeof(int)的整数倍,
	//为简单起见,假设默认建立的是方阵
	//考虑到整除的问题,实际上矩阵分块的大小为矩阵分块的行数与列数之积
	block_col_count = CACHE_LINE_SIZE / sizeof(int);
	for (i = 0; (block_col_count * block_col_count * i * i) <= (L1_SIZE
			* L1_rate / sizeof(int)); i++) {
	}
	block_col_count *= (i - 1);
	block_row_count = block_col_count;
	block_size = block_row_count * block_col_count;
	row_block_count = (n - 1) / block_col_count + 1;//同一列可见的矩阵分块的个数
	col_block_count = (n - 1) / block_row_count + 1;//同一行可见的矩阵分块的个数
	if ((block_left = (float *) malloc(block_size * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for block_left\n");
		exit(1);
	}
	if ((block_right = (float *) malloc(block_size * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for block_right\n");
		exit(1);
	}
	if ((block_res = (float *) malloc(block_size * sizeof(float))) == NULL) {
		printf("Not enough memory to allocate buffer for block_res\n");
		exit(1);
	}
	block_count = row_block_count * col_block_count;

	do {
		pthread_mutex_lock(&mutex1);
		i = global_index;
		global_index++;
		pthread_mutex_unlock(&mutex1);

		//编号为i的矩阵分块在矩阵中的逻辑坐标为(i/block_col_count,i%block_col_count)
		if (i >= block_count)
			printf("Out of matrix!\nExecuting %dth element.\n", i);
		block_row = i / col_block_count;
		block_col = i % col_block_count;

		//若结果矩阵分块位于最后一行,
		//则乘法公式左边矩阵分块的行数是(n - 1) % block_row_count + 1,
		//否则是block_row_count
		if (block_row == row_block_count - 1) {
			row_left = (n - 1) % block_row_count + 1;
		} else {
			row_left = block_row_count;
		}

		//若结果矩阵分块位于最后一列,
		//则乘法公式右边矩阵分块的列数是(n - 1) % block_col_count + 1,
		//否则是block_col_count
		if (block_col == col_block_count - 1) {
			col_right = (n - 1) % block_col_count + 1;
		} else {
			col_right = block_col_count;
		}

		//初始化结果矩阵分块对应的结果矩阵,结果矩阵分块坐标位置为(block_row,block_col),
		//矩阵分块的大小为(row_left,col_right)
		//原理见下文
		m = n * block_row * block_row_count + block_col_count * block_col;//指向矩阵分块在矩阵中的位置
		for (k = 0; k < row_left; k++) {
			for (l = 0; l < col_right; l++) {
				matrix_c2[m + l] = 0;
			}
			m += n;
		}

		//编号为i的结果矩阵分块为坐标位置分别为(block_row,j)
		//和(j,block_col)的矩阵分块的乘积之和
		//+a(block_row,j)*b(j,block_col)
		for (j = 0; j < col_block_count; j++) {
			//若乘法公式右边矩阵分块位于最后一行,
			//则其行数是(n - 1) % block_row_count + 1,
			//否则是block_row_count
			if (j == row_block_count - 1) {
				row_right = (n - 1) % block_row_count + 1;
				col_left = row_right;
			} else {
				row_right = block_row_count;
				col_left = row_right;
			}

			//初始化乘法公式左边的矩阵分块,已知矩阵分块坐标位置为(block_row,j),
			//矩阵分块的大小为(row_left,col_left),
			//第一个元素在原矩阵中的位置是n*block_row*block_row_count+block_col_count*j
			//在矩阵分块中与其同一行的元素可依次赋值
			//下一行的第一个元素相对位移偏移量是n
			m = n * block_row * block_row_count + block_col_count * j;//指向矩阵分块在矩阵中的位置
			for (k = 0; k < row_left; k++) {
				for (l = 0; l < col_left; l++) {
					block_left[k * col_left + l] = matrix_a[m + l];
				}
				m += n;
			}

			//初始化乘法公式右边的矩阵分块,已知矩阵分块坐标位置为(j,block_col),
			//矩阵分块的大小为(row_right,col_right),
			//原理同上
			m = n * j * block_row_count + block_col_count * block_col;//指向矩阵分块在矩阵中的位置
			for (k = 0; k < row_right; k++) {
				for (l = 0; l < col_right; l++) {
					block_right[k * col_right + l] = matrix_b[m + l];
				}
				m += n;
			}
			blocked_matrix_mul(block_left, row_left, col_left, block_right,
					row_right, col_right, block_res);

			//将结果矩阵分块保存到结果矩阵中,结果矩阵分块坐标位置为(block_row,block_col),
			//矩阵分块的大小为(row_left,col_right)
			//原理同上
			m = n * block_row * block_row_count + block_col_count * block_col;//指向矩阵分块在矩阵中的位置
			for (k = 0; k < row_left; k++) {
				for (l = 0; l < col_right; l++) {
					matrix_c2[m + l] += block_res[k * col_right + l];
				}
				m += n;
			}
		}
	} while (global_index < block_count);
	free(block_left);
	free(block_right);
	free(block_res);
}

//@param int thread_num 线程数目
//@param int n 			向量长度
//@return void
//@discribe 使用thread_num个线程并行计算两个长度为n的矩阵乘积
void parallel(int thread_num, int n) {
	int i;
	pthread_t thread[thread_num];
	pthread_mutex_init(&mutex1, NULL);
	for (i = 0; i < thread_num; i++)
		if (pthread_create(&thread[i], NULL, slave, NULL) != 0)
			perror("pthread_create fails");
	for (i = 0; i < thread_num; i++)
		if (pthread_join(thread[i], NULL) != 0)
			perror("Pthread_join fails");
}

//比较串行程序和并行程序运行结果
void compare() {
	int i;
	int index = 0;
	float total = 0.0;
	time_t now;
	now = time(NULL);
	printf("The number of elements in the matrix is %d.\n", n * n);
	fprintf(fp, "\n************************************************\n");
	fprintf(fp, "\n************************************************\n");
	fprintf(fp, "The debug time is %s.\n", ctime(&now));
	for (i = 0; i < n * n; i++) {
		total += (matrix_c1[i]>matrix_c2[i]?(matrix_c1[i]-matrix_c2[i]):(matrix_c2[i]-matrix_c1[i]));
		if (matrix_c1[i] != matrix_c2[i]) {
			fprintf(fp,"matrix_c1[%d]=%f\tmatrix_c2[%d]=%f\n",i,matrix_c1[i],i,matrix_c2[i]);
			index++;
		}
	}
	printf("The number of wrong result is %d\n", index);
	printf("Check Result:%f\n", total);
}

//释放占用的空间
void uninit() {
	fclose(fp);
	free(matrix_a);
	free(matrix_b);
	free(matrix_c1);
	free(matrix_c2);
}

void ComputeArraySSE(float* pArray1, float* pArray2, float* pResult, int nSize) {
	int nLoop;
	int i = 0;
	int j = 0;
	__m128 m1, m2, m3;
	float* p1;
	float* p2;
	float* pr;
	p1 = pArray1;
	p2 = pArray2;
	pr = pResult;
	nLoop = nSize / 4;
	for (i = 0; i < nLoop && pr != NULL; i++) {
		m1 = _mm_loadu_ps(p1);
		m2 = _mm_loadu_ps(p2);
		m3 = _mm_mul_ps(m1, m2);
		_mm_storeu_ps(pr, m3);
		p1 += 4;
		p2 += 4;
		pr += 4;
	}
	j = nSize % 4;
	if (j != 0) {
		printf("The SSE request array size of times of 4.\n");
	}
}

 

分享到:
评论

相关推荐

    矩阵相乘SSE优化算法

    该代码实现采用SSE对矩阵相乘进行优化,一般的矩阵相乘算法与SSE优化算法耗时进行比较比较。

    基于SSE4及多核思想淡入淡出电子相册

    在电子相册的开发中,SSE4可以被利用来优化图像处理算法,比如图片的淡入淡出效果。淡入淡出效果是通过将两幅图像进行透明度混合实现的,这个过程涉及到大量的像素级别的计算。SSE4的向量操作指令可以一次性处理多个...

    FFmpegH264 多线程 优化

    本项目专注于FFmpeg中的H264解码库,通过引入多线程和硬件加速技术如MMX、SSE(Streaming SIMD Extensions)和AVX(Advanced Vector Extensions),来提升解码性能和效率。 多线程技术在FFmpeg中的应用主要是为了...

    基于sse4的电子相册

    标题 "基于sse4的电子相册" 描述的是一个利用 SSE4 指令集优化的 Java 实现的电子相册项目,它专注于并行计算和图像处理,特别是照片的淡入淡出效果。 SSE4(Streaming SIMD Extensions 4)是英特尔推出的一种SIMD...

    基于C++的图像处理算法实现、INTEL上SSE加速、ARM上NEON加速

    SSE指令允许同时处理多个数据元素,如4个单精度浮点数或4个32位整数。在图像处理中,可以使用SSE指令对像素数组进行并行处理,显著提升运算速度。例如,一个SSE指令就能完成一次4像素的滤波操作,相比传统的逐像素...

    多线程案例、SSE编程

    在IT领域,多线程和SSE编程是两个重要的概念,它们在提升程序性能和优化计算密集型任务方面发挥着关键作用。 首先,让我们深入理解多线程。多线程是一种编程模型,允许一个应用程序同时执行多个独立的任务或子任务...

    基于SSE的全局最优K_means算法_董炎焱1

    总结来说,基于SSE的全局最优K-means算法通过优化初始聚类中心的选择,提高了聚类算法的稳定性和准确性,尤其在难以预知合适聚类数的情况下,这种全局搜索策略能有效避免局部最优,找到更接近全局最优的聚类解。...

    基于多线程处理器的H.264软件编码器并行算法.pdf

    文档提出了基于多线程处理器的H.264线程级并行编码算法,目的是为了提高编码速度。该算法通过利用具有编码独立性的帧进行并行编码,从而减少等待时间。实验表明,这种并行算法能够有效提升编码速度。 5. 并行算法...

    并行作业代码----SSE算法矩阵相乘

    并行作业代码——SSE(Streaming SIMD Extensions)算法在矩阵相乘中的应用是高性能计算领域的一个重要主题。SSE是Intel公司为x86架构处理器设计的一种向量处理技术,它允许一次处理多个数据,极大地提高了处理器在...

    一种基于最大最小距离和SSE的自适应聚类算法.docx

    【相关文献】该领域的研究还包括基于最大最小距离的其他聚类算法,如吕佳提出的基于最大最小距离和动态隧道的聚类算法,以及黄晓斌等人提出的模糊最小最大聚类算法,这些工作都致力于改进聚类的效率和准确性,体现了...

    基于SSE4和多核编程的电子相册的实现

    在本项目中,“基于SSE4和多核编程的电子相册的实现”是一个创新性的技术实践,旨在利用现代处理器的高级指令集和并行计算能力来优化电子相册的性能。以下是关于这个项目的详细解释和相关知识点: 1. SSE4指令集:...

    高斯消元法pthread并行算法

    高斯消元法是一种经典线性代数中的解线性方程组的算法,通过一系列行变换将系数矩阵转化为阶梯形或行最简形,从而求解方程组。在这个项目中,高斯消元法被实现了并行化处理,以提高计算效率,特别是对于大规模的线性...

    matlab开发-基于区域的数据域匹配算法

    在本项目中,“基于区域的数据域匹配算法”关注的是通过分析图像的局部区域特性来进行匹配,提高匹配的准确性和鲁棒性。 `stereoRegionGrowingLine.m` 可能是实现区域生长算法的MATLAB脚本。区域生长算法是一种像素...

    编写电子相册要用到的SSE4

    SSE4包含了许多优化指令,这些指令主要设计来增强处理器对单指令多数据流(SIMD)的操作,允许一次处理多个数据元素,显著提高了执行效率。以下是SSE4中一些关键的指令和特性,对于电子相册开发特别有用: 1. **...

    基于SSE指令集的程序设计简介

    Intel公司单指令多数据流式扩展(SSEStreaming SIMD Extensions)技术能够有效增强CPU浮点运算能力Visual Studio .NET 2003提供了对SSE指令集编程支持从而允许用户在C代码中不用编写汇编代码就可直接使用SSE指 ...

    对SSE2模式匹配算法SSE2PatternFind的一点改造优化

    对SSE2模式匹配算法SSE2PatternFind的一点改造优化

    基于遗传算法的改进FCM算法GA-FCM

    《基于遗传算法的改进FCM算法GA-FCM详解》 在数据分析与机器学习领域,聚类算法是一种常用的数据挖掘技术,它通过寻找数据之间的内在关系,将相似的数据归为一类,形成不同的簇。其中,模糊c均值(Fuzzy C-Means,...

    pthread测试工程

    pthread是POSIX线程库,它是跨平台的多线程编程接口,被广泛应用于Linux、Unix和其他类Unix系统。在Windows环境下,通过第三方工具如MinGW或Visual Studio的C++ Linux开发工具,也可以使用pthread库进行开发。在这个...

    sse.rar_SSE_SSE加密_ivmaker_流密码算法_简单加密

    SSE可能采取了一些简化的设计,使得开发者和用户能够在不牺牲太多安全性的前提下,快速集成到他们的应用中。 在实际应用中,SSE可能被用在各种场景,比如网络通信的数据加密、文件存储的安全保护、移动设备的信息...

Global site tag (gtag.js) - Google Analytics