矩阵乘法运算效率Word文档格式.docx

上传人:b****5 文档编号:18841245 上传时间:2023-01-01 格式:DOCX 页数:24 大小:212.32KB
下载 相关 举报
矩阵乘法运算效率Word文档格式.docx_第1页
第1页 / 共24页
矩阵乘法运算效率Word文档格式.docx_第2页
第2页 / 共24页
矩阵乘法运算效率Word文档格式.docx_第3页
第3页 / 共24页
矩阵乘法运算效率Word文档格式.docx_第4页
第4页 / 共24页
矩阵乘法运算效率Word文档格式.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

矩阵乘法运算效率Word文档格式.docx

《矩阵乘法运算效率Word文档格式.docx》由会员分享,可在线阅读,更多相关《矩阵乘法运算效率Word文档格式.docx(24页珍藏版)》请在冰豆网上搜索。

矩阵乘法运算效率Word文档格式.docx

1.1研究背景及意义3

1.2研究内容4

第二章基础知识5

2.1矩阵乘法运算5

2.2高速缓冲存储器6

2.2.1设置Cache的理论依据6

2.2.2Cache的体系结构8

2.2.3Cache的相关知识8

2.3开发平台9

第三章测试程序10

3.1.数据区的设定10

3.2.程序执行时间的计算方法10

3.3.测试程序的运行结果保存方式11

3.4.测试程序代码12

第四章结果和分析16

4.1实验结果图16

4.2实验结果分析17

第五章改进的矩阵乘法运算18

5.1分块的矩阵乘法运算18

5.2分块的矩阵乘法运算实验结果和分析19

第六章总结21

参考文献23

致谢25

第一章概述

1.1研究背景及意义

在应用程序中,如何提高程序的效率?

这是很现实的问题。

应用程序作为人们与计算机“交谈”的工具,其运行速度是一个十分重要的指标。

人们总是希望应用程序执行速度快些,以便达到最佳的运行效果。

随着计算机硬件的发展,微机的速度越来越快,运行软件的能力也越来越强。

计算机中程序的运行速度主要受计算机硬件和软件影响,提高计算机程序的运行速度,一般可以从以下几个方面着手:

1.改进算法

要想提高程序运行效率,改进算法是最关键的。

算法是影响程序运行效率的主要因素,在编写不同程序时要选择适当的算法。

算法是计算机求解特定问题的方法和步骤,是指令的有限序列。

通常一个问题可以有多种算法,而一个好的算法通常应该具有下列5个基本特性:

正确性、健壮性(鲁棒性)、可读性、高效率、低存储空间。

2.改进程序

同一个算法,实现的程序因为书写不同,其程序的运行效率也是不一样。

而提高程序效率的方法又有很多。

以C/C++语言为例,可以通过以下方法来提高程序的运行效率:

(1)采用自加和自减运算

说到对C/C++语言的优化,人们马上会想起X++和X――(或++X、――X),它们的功能虽与X=X+1、X=X-1相同,但二者的运行效率却截然不同。

前者不仅运算速度快,而且占用RAM的时间更少。

(2)巧用寄存器变量

寄存器变量是保存在CPU内部寄存器中的,其访问速度要比内存变量快得多。

若将寄存器变量用于关键的内层循环控制变量,就会大大提高程序的运行速度。

(3)适当采用指针和数组下标

指针是C/C++语言的一个重要特色,正确使用指针,可以使程序简洁、高效、紧凑。

利用指针变量比直接利用数组下标来访问数组程序运行要快得多。

(4)合理使用内联函数

在C++中,为了解决一些频繁调用的小函数大量消耗栈空间的问题,特别地引入了inline修饰符,表示为内联函数。

内联函数和宏很类似,其区别在于,宏是由预处理器对宏进行替代,而内联函数则是通过编译器控制来实现的,而且内联函数是真正的函数,只是在需要用到的时候,像宏一样地展开,所以取消了函数的参数压栈,减少了调用的开销。

你可以像调用函数一样来调用内联函数,而不必担心会产生类似处理宏的一些问题。

(5)采用位运算

C/C++语言可以用来开发系统软件,因此具有汇编语言所能完成的一些功能。

所谓位运算是指进行二进制位的运算,这项功能不仅有着其他高级语言无法相比的优越性,而且位运算速度快,在某些应用中还有着意想不到的作用。

3.硬件环境

除了上述方法之外,结合硬件环境也可以提高程序的运行效率。

在计算机硬件对计算速度的影响上,计算机的硬件是提高应用程序计算速度和运行效率的重要途径之一。

程序员在设计程序时,应当充分认识到计算机系统的重要性及实现细节,设计出高效、健壮、可移植的程序,从而能提高程序的运行速度。

众所周知,内存的存取速度极大地制约着计算机的处理能力。

目前,CPU的速度越来越快,尽管内存的速度也不断提高,但远远落后于CPU的处理速度。

因而,CPU要花大量的时间等待内存访问延迟,因而内存存取速度是计算机速度的一个瓶颈。

为了提高内存的存储速度,越来越多的计算机采用高速缓存Cache技术。

因而,如何有效地利用Cache的特性,提高程序的运行效率,已经成为一个热点问题。

本文通过矩阵乘法运算相关代码的运行时间,讨论高速缓冲存储器对程序运行速度的影响。

1.2研究内容

本文共分为6章,其具体组织如下:

第一章是绪论。

简要介绍了高速缓冲存储器对程序运行速度的影响分析研究的研究背景和意义,并且介绍了以C语言为例的提高程序运行效率的方法,在总结这些工作的基础上提出本文的研究思路与内容。

第二章基础知识。

简要介绍了与本文研究相关的矩阵乘法运算和高速缓存Cache的有关知识,主要包括矩阵乘法运算的六个不同程序版本,Cache的工作原理,Cache的命中率等,并在最后对实验的开发环境VisualC++6.0做一个简单的介绍。

第三章测试程序。

首先对构成矩阵的数组做一个说明,其次介绍4种用于测试程序运行时间的方法及函数,接着说明了程序实验结果的存放形式和方法,最后给出了完整的测试程序代码。

第四章结果及分析。

以表格形式呈现实验结果,根据实验结果,绘制图表。

对程序从空间局部性和时间局部性两方面进行分析,以验证实验结果。

第五章改进的矩阵乘法运算。

针对程序的空间局部性和时间局部性随着数组的增大而降低的情况,提出了分块的思想,并且根据分块思想构建矩阵乘法运算的程序,相应的实验证明分块能够较好的提高程序的局部性,提高程序的运行效率。

第六章总结。

总结本文所做的主要研究工作,并且对利用局部性原理和高速缓存提高程序的运行效率提出了建议。

第二章基础知识

2.1矩阵乘法运算

考虑两个矩阵的乘法问题C=

假设输入是两个规模为

的矩阵A和B,输出为

的矩阵C。

例如,n=2,那么

A=

,B=

,C=

其中:

矩阵乘法通常使用三个嵌套的循环来实现的,分别用i,j,k来标识。

for(i=0;

i<

n;

i++)//ijk版本

for(j=0;

j<

j++)

{sum=0.0;

for(k=0;

k<

k++)

sum+=A[i][k]*B[k][j];

c[i][j]+=sum;

}

如果改变循环的次序,对代码进行一些其他的小改动,就能得到矩阵乘法的六个在功能上等价的版本,每个版本以循环的顺序唯一标识。

其他五个版本的算法描述分别如下:

j++)//jik版本

i++)

j++)//jki版本

for(k=0;

k++)

{r=B[k][j];

for(i=0;

i++)

c[i][j]+=A[i][k]*r;

k++)//kji版本

{r=B[k][j];

k++)//kij版本

{r=A[i][k];

for(j=0;

j++)

c[i][j]+=r*B[k][j];

i++)//ikj版本

以上六个版本非常相似,因为加法具有结合律,从矩阵运算的角度,每个版本的计算结果完全一样。

A和B中的

个元素每一个都要读n次,C中的每一个元素都对n个值求和。

以ijk版本为例,该矩阵乘法版本是逐项计算

,计算一个

需要(n-1)次加法和n次乘法,因而逐项计算

共需执行

(n-1)次加法和n

次乘法,所以算法的时间复杂度是O(n

)。

我们可以很容易的分析出,矩阵乘法运算六个版本的工作复杂度都为O(n

2.2高速缓冲存储器

2.2.1设置Cache的理论依据

在计算机技术发展过程中,主存储器存取速度一直比中央处理器操作速度慢,尤其近年来,CPU的运行速度每年以40%的速度上升,而主存储器的存取速度每年仅上升10%左右,不能充分发挥中央处理器的高速处理能力,使得整个计算机系统的工作效率受到影响。

有很多方法可以用来缓和中央处理器和主存储器之间速度不匹配的问题,常用的方法之一是在存储层次上采用高速缓冲存储器,如图2.1所示,利用高速缓存Cache来缩小处理器和存储器之间的速度差距。

图2.1存储系统的多级层次结构

高速缓冲存储器能缓和中央处理器和主存储器之间速度不匹配的问题的原因是程序的局部性原理。

程序的局部性原理是指在一段时间内,程序的执行仅限于访问存储器的某一区域。

Hennessy和Patterson提出了90—10规则,典型程序在其10%的代码上可能要消耗其执行时间的90%,其90%的访存集中在10%的区域,这就是程序的局部性。

产生局部性原理的原因是:

(1)除了调用和转移指令(它们在整个程序中只占很小的一部分)以外,程序的执行是顺序的,从而,在大多数情况下,下一条指令可以在当前指令之后立即读入处理器。

(2)大多数情况下,程序中的连续过程调用深度较小,程序中出现一连串不间断的过程调用后跟着相应的一连串过程返回的操作是十分罕见的。

这样,在较短时间内,访问的指令将集中在少数几个过程中。

(3)大部分迭代过程由相对很少的几条指令重复执行来实现。

在迭代期间,计算集中在一段很短的连续程序内。

(4)在许多程序内,很多计算涉及到处理数据结构,例如数组和记录,连续的访问这些数据结构也能体现出访存的局部性。

程序的局部性原理又分为时间局部性和空间局部性。

时间局部性是指如果程序中的某条指令被执行,则不久之后该指令可能再次被执行;

如果某数据被访问,则不久之后该数据可能再次被访问。

空间局部性是指如果程序访问了某个存储单元,则不久之后其附近的存储单元也将可能被访问。

2.2.2Cache的体系结构

根据局部性原理,上世纪五十年代就引入了存储层次的概念,在一些计算机中采用容量较小但是访问较快的存储器来缓存部分经常使用的指令或数据,与主存储器构成一个有机的整体,从而弥补主存储器速度上的不足,提高整个系统性能。

自从第一次在IBM/360商用计算机中采用高速缓冲存储器,并且提出Cache这个名词之后,Cache由于其管理能够自动由硬件实现,逐渐发展并成为降低处理器和存储器之间速度差距的一种重要方法。

随着Cache的发展,其层次变锝越来越复杂,多级Cache逐渐成Cache层次的主流。

图2.2给出来目前体系结构中一种典型的Cache层次结构图,整个Cache层次分为两级:

一级Cache一般速度较快,但容量较小,用于存储指令的Cache和用于存储数据的Cache各自分开;

二级Cache一般容量较大,但速度较慢,在这一级Cache指令和数据通常混合存储。

图2.2一种典型的Cache层次结构图

2.2.3Cache的相关知识

Cache在计算机系统中是按块管理的,Cache和主存被分割成大小相同的块,指令或者数据以块为单位调入Cache。

Cache能容纳多个信息块,若CPU发出读请求,则将含有指定单元的一个信息块从主存送入Cache中。

当CPU读取该信息块中的任何单元时,就可以直接从中取出,这称为命中。

当访问的单元不在Cache中,这称为不命中或失效。

在程序执行期间,设Nc表示读写Cache的次数,Nm表示读写主存的次数,则Cache的命中率H的计算如下:

H=Nc/(Nc+Nm)

若t1表示命中时的Cache访问时间,t2表示未命中时的主存访问时间,1-H表示未命中率,则平均访问时间T为:

T=H×

t1+(1-H)×

t2

理解Cache的工作原理需要明白Cache的关联规则、查找方法、替换算法和写策略。

1.关联规则

在一般的系统中,主存容量远远大于Cache的容量,当将主存中一个块的内容调入Cache的时候,如何选择相应的Cache块是首先需要决定的问题,关联规则就是用来解决这个问题的。

2.查找方法

在访问Cache时如何确定Cache中是否存在相应的块,并且如果存在时怎样找到该块的位置,这都需要查找方法来解决。

实际中一般通过查找目录表来实现,在Cache中留出一部分空间,设置一个目录表,记录每一个Cache块的信息,包含该Cache块是否放置有效信息和放置的块在主存中的地址(当该块放置了有效信息时)。

3.替换算法

一般主存中块的数目远远大于Cache中块的数目,当Cache不命中需要将主存中的块调入Cache时,就会出现该块可能的位置都已经被别的块占用,这时候就需要将这些块中的某一块调出,以便新的块调入。

替换算法就是用来如何选择被调出的块,常用的替换算法有下面三种:

(1)随机法:

选择被调出的块时,随机地从可选择的块中选择出一块。

这种方法的优点是简单,便于硬件实现,但是不考虑过去的使用情况,反应不了程序的局部性。

(2)先进先出法(FIFO,FirstInFirstOut):

选择被调出的块时,从可选择的块中选择最早调入的块。

这种方法利用各块调入的“历史”信息,较容易实现,但是也不能反应程序的局部性。

(3)最近最少使用法(LRU,LeastRecentlyUsed):

选择被调出的块时,从可选择的块中选择最近最少使用的那一块。

由于实现困难,通常都是选择最久没有被访问过的块,这种方法能够较好的反应程序的局部性。

4.写策略

由于Cache块中的内容是主存中块的内容的一个副本,处理器执行写指令时,需要更新Cache块中的内容和主存块中的内容,怎么样执行这个两个更新则是Cache写策略需要解决的问题。

根据局部性原理知道,局部性比较好的程序更容易有较高的命中率,而命中率较高的程序倾向于比命中率低的程序运行的更快。

Cache的存在,使得程序员面对一个既有Cache速度又有主存容量的存储系统,在编程中应该充分利用程序访问局部性原理,提高cache的命中率,从而提高程序的执行效率。

2.3开发平台

VisualC++6.0是Microsoft公司提供的在Windows环境下进行程序开发的C/C++编译器,是MicrosoftVisualStutio套装软件的一个有机组成部分。

VisualStutio把所有的VisualC++工具结合在一起,集成为一个整体,通过一个由窗口、对话框、菜单、工具栏、快捷键及宏组成的和谐系统,可以观察和控制整个开发过程。

本文进行实验的测试环境采用VisualC++6.0平台,运用C语言构建测试程序。

第三章测试程序

基于上述矩阵乘法运算的6种不同版本程序代码,编写了一个测试程序,用于检测6个不同版本程序代码的执行时间。

3.1.数据区的设定

利用rand()函数随机生成double类型数据,组成

的double类型数组A和B,并以行优先存储数组数据,当按行访问数据时,访问的空间局部性优于按列访问。

并以此为基础构建

矩阵A和B,n的最大值为200。

3.2.程序执行时间的计算方法

用于测算程序执行时间的方法有很多,下面具体给出4种方法。

1.GetTickCount()函数

这个是windows里面常用来计算程序运行时间的函数。

DWORDdwStart=GetTickCount();

//这里运行程序代码

DWORDdwEnd=GetTickCount();

则(dwEnd-dwStart)就是程序运行时间,以毫秒为单位。

缺点是这个函数只精确到55ms,1个tick就是55ms。

2.time()函数

time(&

rawtime)函数获取当前时间距1970年1月1日的秒数,以秒计数单位,存于rawtime中。

3.通过API进行测量

VC++6.0中,使用“MFCAppWizard”和“Win32Ap2plication”等生成的项目,可使用Win32API来测量程序的运行时间。

但同时也大大增加了一个项目所需的空间,因为使用API需要包含若干相当大的文件。

在Winbase.h中,有下面的API函数声明:

BOOLQueryPerformanceCounter(LARGEINTEGER3lpPerformance2Count)

4.clock()函数

C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t。

在MSDN中,查得对clock函数定义如下:

clock_t 

clock( 

void 

);

这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock 

tick)数,在MSDN中称之为挂钟时间(wal-clock)。

其中clock_t是用来保存时间的数据类型,在time.h文件中,我们可以找到对它的定义:

#ifndef 

_CLOCK_T_DEFINED

typedef 

long 

clock_t;

#define 

#endif

很明显,clock_t是一个长整形数。

在time.h文件中,还定义了一个常量CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元,每过千分之一秒(1毫秒),调用clock()函数返回的值就加1。

其定义如下:

#define 

CLOCKS_PER_SEC 

((clock_t)1000)

本文测试程序的运行时间使用的clock()函数,精度是毫秒级。

由于测试数据受计算机操作系统等影响,每次执行程序所得的测试数据不尽相同,所以采取的方法是让每段程序运行N次,用运行N次的总时间除以运行次数N,得到平均每次的运行时间。

在程序开始运行时用begin=clock()记录时间,结束时用end=clock()记录,得到运行时间time=(double)(end-begin)/CLOCKS_PER_SEC/N。

3.3.测试程序的运行结果保存方式

测试程序的运行结果用程序写入文本文件result.txt中。

代码如下:

FILE*fp;

if((fp=fopen("

result.txt"

"

wb+"

))==NULL)

{

printf("

Cannotopenfile!

Printanykeyexit!

"

);

getchar();

exit(0);

fprintf(fp,"

数组大小:

%d\"

n);

for(i=0;

7;

%f\n"

time[i]);

fclose(fp);

运行结果存放形式如图2.3所示

图2.3运行结果存放图

3.4.测试程序代码

#includ

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 冶金矿山地质

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1