#include
大矩阵的计算时间主要消费在IO访问上,可以利用cache的空间局部性原理,对矩阵的IO进行优化,本文主要是从行列相乘入手,利用转置和分块法进行优化
5. 预取数据程序
/*
* auth = peic
* 预取数据
*/
#include
#include
#include
#include
#define N 1024 * 1024 * 256
#define PREFETCH 2
using namespace std;
int main()
{
int* x = new int[N];
int* y = new int[N];
int i, sum = 0;
double start_time = 0, end_time = 0;
start_time = (double)clock() / CLOCKS_PER_SEC;
for(i = 0; i < N; i+=(PREFETCH/2))
{
sum += (*(x+i)) * (*(y+i));
}
end_time = (double)clock() / CLOCKS_PER_SEC;
cout << "without prefetch, time = " << (end_time - start_time) << "s" << endl;
start_time = (double)clock() / CLOCKS_PER_SEC;
for(i = 0; i < N-PREFETCH; i+=(PREFETCH/2))
{
_mm_prefetch(x+PREFETCH, _MM_HINT_T0);
_mm_prefetch(y+PREFETCH, _MM_HINT_T0);
sum += (*(x+i)) * (*(y+i));
}
end_time = (double)clock() / CLOCKS_PER_SEC;
cout << "* with prefetch, time = " << (end_time - start_time) << "s" << endl;
//system("pause");
return 0;
}
程序运行结果:
6. cache大小测试程序及其结果
cache行大小测试
/*
* auth = peic
* 测试cache行的大小
*/
#include
#include
#include
#include
#define KB (1024)
#define MB (1024 * 1024)
#define SIZE (64 * MB)
using namespace std;
//定义获取微秒时间的函数
double getTime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
//获取秒
double sec = (double)tv.tv_sec;
//获取微秒
double usec = (double)tv.tv_usec;
//返回微秒数
return sec * 1000000 + usec;
}
//定义遍历数组函数
int traverse(int* array, int len, int interval)
{
for(int i = 0; i < len; i += interval)
{
*(array+i) *= 10;
}
return 1;
}
int main()
{
int *array = (int *)malloc(SIZE * sizeof(int));
double begin_time;
double end_time;
//数组初始化
for(int i = 0; i < SIZE; i++)
{
*(array+i) = i;
}
//使用不同步长遍历数组,输出程序执行时间
for(int interval = 1; interval < 64 * KB; interval *= 2)
{
begin_time = getTime();
traverse(array, SIZE, interval);
end_time = getTime();
//删除步长,指令数,每条指令的花费
printf("interval: %5d, IP:%8d, average cost %10lf us\n", interval, SIZE/interval, (end_time - begin_time) / (SIZE/interval));
}
return 1;
}
程序运行结果(时间发生明显跳变就是cache断行,据此推测cache的行大小,块测试同理):
cache块大小测试
/*
* auth = peic
* 测试不同级cache的大小
*/
#include
#include
#include
#include
#define KB (1024)
#define MB (1024 * 1024)
#define SIZE (64 * MB)
using namespace std;
//定义获取微秒时间的函数
double getTime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
//获取秒
double sec = (double)tv.tv_sec;
//获取微秒
double usec = (double)tv.tv_usec;
//返回微秒数
return sec * 1000000 + usec;
}
//定义遍历数组函数
//size为设定的cache块大小
int traverse(int* array, int size)
{
//获得size的二进制的后几位
int len = size - 1;
for(int i = 0; i < SIZE; i++)
{
//cache行的大为16
array[(i*16) & (len)] *= 3;
}
return 1;
}
int main()
{
int *array = (int *)malloc(SIZE * sizeof(int));
double begin_time;
double end_time;
//数组初始化
for(int i = 0; i < SIZE; i++)
{
*(array+i) = i;
}
for(int size = 1024; size < SIZE; size *= 2)
{
/*
if(size == 2*MB/sizeof(int))
{
size = 3*MB / sizeof(int);
}
*/
begin_time = getTime();
traverse(array, size);
end_time = getTime();
//输出不同cache块的花费
printf("size: %8d, cost %10lf us\n", size, (end_time - begin_time));
}
return 1;
}
程序运行结果(3级cache,3级跳变):
7. 矩阵运算优化
普通运算:
/*
* auth = peic
* 直接进行矩阵乘法运算,未做任何优化
*/
#include
#include
#include
#define N 1024
void MartixMultip(int* A, int* B, int* C, int size)
{
int i, j, k;
for(i=0; i < size; i++)
{
for(j=0; j
转置后再计算
/*
* auth = peic
* 对矩阵乘法进行优化, 对矩阵进行转置
*/
#include
#include
#include
#define N 1024
void MartixMultip(int* A, int* B, int* C, int size)
{
int i, j, k;
//先对矩阵进行转置
int *tmp = (int *)malloc(sizeof(int) * size * size);
for(i=0; i
分块运算
/*
* auth = peic
* 矩阵乘法优化,对矩阵进行分块
*/
#include
#include
#include
#define N 1024
int min(int a, int b)
{
return a < b ? a : b;
}
void MartixMultip(int* A, int* B, int* C, int size, int block)
{
int i, j, k;
//先循环分块
for(int ii=0; ii < size; ii += block)
{
for(int jj=0; jj
运算结果:
从结果上看,转置的优化很明显。分块依赖于矩阵的规模和分块规模的选择,因为分块带来中间的消耗,需要寻找最佳的分块方案。
举这些例子只是想表明,有时候,很简单的一些方法就能极大的提高程序的运行效率。