C中实现矩阵乘法的一种高效的方法_C 语言

如何计算矩阵乘法,这个大家都知道。通常情况下,我们都是用以下代码实现的

复制代码 代码如下:

for(i=0;i<n;++i)
    for(j=0;j<n;++j){
        sum=0;
        for(k=0;k<n;++k)
            sum+=A[i][k]*B[k][j];
        C[i][j]+=sum;
}

但是考虑了高速缓存的问题后,其实有一种更好的实现方式:

复制代码 代码如下:

for(i=0;i<n;++i)
    for(k=0;k<n;++k){
        r=A[i][k];
        for(j=0;j<n;++j)
            C[i][j]+=r*B[k][j];
}

细看一番就会发现这两种实现语义是等价的,但是后者的实际运行效率却比前者高。

那为什么会如此呢?

那是因为CPU读数据时,并不是直接访问内存,而是先查看缓存中是否有数据,有的话直接从缓存读取。而从缓存读取数据比从内存读数据快很多。

当数据不在缓存中时,CPU会将包含数据在内的一个数据块读到缓存,如果程序具有良好空间局部性,那么第一次cache miss后,之后的几次数据访问就可以直接在缓存中完成。除了空间局部性(程序倾向于引用与当前数据邻近的数据)之外,还有时间局部性(程序倾向于引用最近被引用过的数据)。

回到矩阵乘法。(我们只考虑内循环)

前者对矩阵A,有良好的空间局部性,假设一次能缓存四个元素,则每次迭代对于A只有0.25次miss,但是对于B,则不然,因此B是按列访问的,每次访问都会miss,因此每次迭代总的miss数是1.25。

后者对于矩阵C和矩阵B都有良好的局部性,每次迭代都只有0.25词miss,因此总的miss数是0.5。后者每次迭代多了一次存储(对C[i][j]写入),但是即便如此,后者的运行效率也比前者高。

总而言之,要想程序跑得快,就要在程序中多利用局部性,让缓存hold住你的数据,减少访存次数。要知道CPU可以在3个时钟周期内访问到L1 cache,10个时钟周期左右的时间访问到L2 cache。访问内存却要上百个时钟周期,孰快孰慢,很清楚了吧?

时间: 2024-10-23 14:15:15

C中实现矩阵乘法的一种高效的方法_C 语言的相关文章

C++中的多态与虚函数的内部实现方法_C 语言

1.什么是多态 多态性可以简单概括为"一个接口,多种行为". 也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可以用自己的方式去响应共同的消息.所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数.这是一种泛型技术,即用相同的代码实现不同的动作.这体现了面向对象编程的优越性. 多态分为两种: (1)编译时多态:主要通过函数的重载和模板来实现. (2)运行时多态:主要通过虚函数来实现. 2.几个相关概念 (1)覆盖.

C++实现将一个字符串中的字符替换成另一个字符串的方法_C 语言

本文实例讲述了C++实现将一个字符串中的字符替换成另一个字符串的方法,分享给大家供大家参考.具体方法如下: 题目要求: 原地实现字符串中的每个空格替换成"%20",例如输入"We are happy", 输出"We%20are%20happy" 被替换的字符串当然不仅仅是空格,上面只是个例子 这是道很好的题目,也是百度面试中的一道题,题目不难,但是问题得考虑全面.这里给出如下实现代码: #include <iostream> #inc

MFC中动态创建控件以及事件响应实现方法_C 语言

本文实例讲述了MFC中动态创建控件以及事件响应实现方法,分享给大家供大家参考.具体实现方法如下: 动态控件是指在需要时由Create()创建的控件,这与预先在对话框中放置的控件是不同的. 一.创建动态控件: 为了对照,我们先来看一下静态控件的创建. 放置静态控件时必须先建立一个容器,一般是对话框,这时我们在对话框编辑窗口中,从工具窗口中拖出所需控件放在对话框中即可,再适当修改控件ID,设置控件属性,一个静态控件就创建好了,当对话框被显示时,其上的控件也会显示. 静态控件不需要调用Create()

讲解C++中的枚举类型以及声明新类型的方法_C 语言

C++枚举类型如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型.所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内.声明枚举类型用enum开头.例如: enum weekday{sun, mon, tue, wed, thu, fri, sat}; 上面声明了一个枚举类型weekday,花括号中sun, mon, -, sat等称为枚举元素或枚举常量.表示这个类型的变量的值只能是以上7个值之一.它们是用户自己定义的标识符. 声明枚举类型

c++中的消息框messagebox()详细介绍及使用方法_C 语言

1.MessageBox("这是一个最简单的消息框!"); 2.MessageBox("这是一个有标题的消息框!","标题"); 3.MessageBox("这是一个确定 取消的消息框!","标题", MB_OKCANCEL ); 4.MessageBox("这是一个警告的消息框!","标题", MB_ICONEXCLAMATION ); 5.MessageBox(&

VC6.0打开文件以及向工程中添加文件时程序崩溃自动退出解决方法_C 语言

换了一台电脑,vc6.0程序中,点击打开文件以及向工程中添加文件时,程序竟然崩溃自动退出了. 不知什么原因,安装相同的vc程序,本本竟然出现此缘故.但是这个操作又是自己经常用到的,所以不得不解决. 与上一台电脑不同的是,此电脑是win7系统,而上一个则是xp系统.此电脑office是2010版本,而上一个则是WPS:于是乎,在网上查资料,来解决. 看到网上也有类似的问题,有的说是win7系统原因,有的说是office2007版本缘故,有的说是viso缘故.总之,这几种说法,我都符合.win7系统

在C++程序中开启和禁用Windows设备的无线网卡的方法_C 语言

1.列出当前网卡:SetupDiEnumDeviceInfo 2.找出当前无线网卡的名字(用natvie wifi api) 3.卸载\安装此驱动 问题: log为:SetupDiSetClassInstallParams failed. -536870347   完整代码如下: // ControlWirelessCard.cpp : Defines the entry point for the console application. // #include "stdafx.h"

C语言中等待socket连接和对socket定位的方法_C 语言

C语言listen()函数:等待连接头文件: #include <sys/socket.h> 定义函数: int listen(int s, int backlog); 函数说明:listen()用来等待参数s 的socket 连线. 参数backlog 指定同时能处理的最大连接要求, 如果连接数目达此上限则client 端将收到ECONNREFUSED 的错误. Listen()并未开始接收连线, 只是设置socket 为listen 模式, 真正接收client 端连线的是accept()

求斐波那契(Fibonacci)数列通项的七种实现方法_C 语言

一:递归实现使用公式f[n]=f[n-1]+f[n-2],依次递归计算,递归结束条件是f[1]=1,f[2]=1.二:数组实现空间复杂度和时间复杂度都是0(n),效率一般,比递归来得快.三:vector<int>实现时间复杂度是0(n),时间复杂度是0(1),就是不知道vector的效率高不高,当然vector有自己的属性会占用资源.四:queue<int>实现当然队列比数组更适合实现斐波那契数列,时间复杂度和空间复杂度和vector<int>一样,但队列太适合这里了,