`
yzd
  • 浏览: 1818398 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

你的for循环真的高效吗——优化for循环第二章

 
阅读更多
<p>我始终相信,人类最伟大的发明就是汽车和计算机,对于一部汽车,我们如果不经过专业的了解汽车内部结构的工程师调试,就算你是保时捷,也达不到理想的速度。对于计算机来说,我始终觉得,我们很多人只是明白程序的写法,例如一个程序:</p>
<p><textarea cols="50" rows="15" name="code" class="cpp">#include &lt;stdio.h&gt;
char *hello = "hello";
char *__ = ",";
int main()
{
char *world = "world";
printf("%s%s%s/n",hello,__,world);
return 0;
}</textarea></p>
<p>我们都知道这个程序输hello,world,对了!我们却不知道字符串hello,字符串__,字符串world是不是在一个位置,如果你觉得,不必关心这些了,反正程序已经达到了效果,那么请你离开这个网页,不要浪费你的时间。</p>
<p>回到计算机上来,我们要让计算机发挥最大的性能,就必须也要像“了解汽车内部结构的工程师”一样的了解计算机,我时常赞叹计算机设计的优美。为了加快访问的速度,我们加入了缓存,缓存的加入,就像我们在超高速行驶汽车时,再优秀的发动机也不能完全燃烧汽油,而且速度越高,会由于空气的不足,导致汽油燃烧不尽,从而成了汽车提速的瓶颈,然后,我们在汽车的身上再多开几个洞(这就是你为什么见到豪华跑车身上都有几个洞),把空气压缩进发动起,从而提高速度。这和计算机缓存有着异曲同工之妙,好好利用,就相当于,你比别人跑得更快。</p>
<p></p>
<p>对于计算机缓存来说,我们都知道他是高速但是昂贵的(难道在跑车开几个洞就不昂贵了吗,是不是很像?),对于缓存优化,我们紧紧介绍L1缓存的优化(如果你会好好利用,它足以让你的程序运行时间降低很多)。</p>
<p><img src="http://hi.csdn.net/attachment/201106/3/0_13070653910au1.gif" alt=""></p>
<p></p>
<p>什么是L1缓存,在计算机CPU中,我们为了提高程序的执行效率和CPU加载相关代码和数据的速度所加入的一种存储设备,当然,一般CPU会有L2缓存,甚至是L3缓存,很多人的电脑L1缓存的一行只能放入16个字节,就是只能放入4个整数型数据(gcc 4.4版本,32位计算机,酷睿2双核,2.0G赫兹处理速度),可以把计算机L1缓存看成一个二维矩阵,写个程序给你看看吧!</p>
<p><span style="font-family: monospace; font-size: x-small;"><span style="white-space: pre-wrap;"><textarea cols="50" rows="15" name="code" class="cpp">#include &lt;stdio.h&gt;
int main()
{
int array[4][4];
int i,j;
for(i=0;i&lt;4;i++)
  for(j=0;j&lt;4;j++)
   array[i][j]=0;
return 0;
}</textarea></span></span></p>
<p></p>
<p><span style="font-family: monospace; font-size: x-small;"><span style="white-space: pre-wrap;"><textarea cols="50" rows="15" name="code" class="cpp">#include &lt;stdio.h&gt;
int main()
{
int array[4][4];
int i,j;
for(i=0;i&lt;4;i++)
  for(j=0;j&lt;4;j++)
   array[j][i]=0;
return 0;
}</textarea></span></span></p>
<p>我们比较着两个程序,当然,你运行时发现不了谁对谁快,但是,我慢慢给你说,这只是一个例子,上面的程序是先按照行,再按照列输出的,我们叫程序1,下面的是先输出列,在输出行,我们叫程序2。我告诉过你们,你们可以把这个缓存想象成一个n×4的矩阵,然后我们就在把我们的程序所用到的数据加载到L1中去。作为一个程序员,我们要知道,程序是局部存储原理,就是,我们把相似的数据存放在一起,这也就是为什么C语言会有这么多不同的段,什么BSS啦,什么data啦,等等,我们也把经常访问的数据段放在一起,因为程序都是以“块”的形式来存取的,这就是为什么硬盘读取一般都是4K的页?同样,内存给缓存还是以这样的块来读取的,只不过一个块的大小逐渐降低,具体怎么递减,别问我,我也不知道!</p>
<p>好了,我告诉这两个程序的效率吧,假设我们的L1缓存时一个类似的2×4的矩阵(实际没这么小,为了举例子方便),我们看第一个程序,首先,我们先加载array[0][0],在每次加载的时候,我们都会检查我们的缓存中是不是有我们需要的数据,当然,我们第一次加载我们所要的数据,我们的缓存中当然没有我们要的数据了,第一次加载我们把array[0][0],array[0][1],array[0][2],array[0][3]这四个缓存数组加载到需要加载的缓存行,也就是第一行或者第二行,怎么加载的,在Linux中,是有一个时间限制的和引用次数来双重决定的,我们并不讨论这个问题,我们假设加载到第一行上,就这四个数据,然后我们的程序1接着访问array[0][1],检查在缓存中有我们要的数据,我们就不用在加载了,直接给寄存器就好了,当到了array[1][0]时,没有我们要的数据,我们就再把它加载到第二行中去,是array[1][0],array[1][1],array[1][2],array[1][3]这四个数据,访问完了这四个数据时,我们再接着访问array[2][0],缓存中还是没有,我们就把它加载打第一行中去,这样,我们一共就加载了4次数据。</p>
<p>但是程序2就不是这样了,我们先访问array[0][0],没有,照样我们加载到缓存行1中,当然,计算机还是加载array[0][0],array[0][1],array[0][2],array[0][3],但是,我们接着就访问array[1][0],缓存中没有,我们就在加载array[1][0],array[1][1],array[1][2],array[1][3]这四个数据到缓存行2中去,然后在访问array[3][0],还是没有,在加载,以此类推,我们加载了一共4×4次数据,效率怎么样,我就用告诉你:我曾问大师,汽车提高速度的瓶颈是什么?大师说:瞬间燃烧的汽油量和热量的利用率。我默然低下头,那么计算机的瓶颈就是能不能大规模的提取我要的数据和IO操作的效率。</p>
<p>当然实际上我们的缓存L1一般是没这么小的,但是你看这个程序:</p>
<p><textarea cols="50" rows="15" name="code" class="cpp">#include &lt;stdio.h&gt;
int main()
{
int i,j;
int array1[256][256];
int array2[256][256];
for(i=0;i&lt;256;i++)
  for(j=0;j&lt;256;j++)
   array1[i][j]=array2[i][j];
return 0;
}</textarea></p>
<p>我们得加载两个数组,每个数组的大小是256×256,这个超过L1缓存的大小,效率就显现出来了!要不你写成这样试试:</p>
<p><span style="font-family: monospace; font-size: x-small;"><span style="white-space: pre-wrap;"><textarea cols="50" rows="15" name="code" class="cpp">#include &lt;stdio.h&gt; 
int main() 

int i,j; 
int array1[256][256]; 
int array2[256][256]; 
for(i=0;i&lt;256;i++) 
  for(j=0;j&lt;256;j++) 
   array1[j][i]=array2[j][i]; 
return 0; 
}</textarea></span></span></p>
<p>运行时加上系统API函数的时间函数,读者自己实验看看!</p>
<p></p>
<p>关于L1缓存的大小,每个CPU不同,这个也是不同的,所以不敢妄加告诉大家。</p>
<p>结尾话:</p>
<p>贝多芬说,音乐家是离上帝最近的人,其实他漏掉了另一种——程序员,为什么你没成为计算机中“贝多芬”?原因很简单,贝多芬曾因为弹钢琴她的手指缝发热而不得不用水浸湿后再弹。写程序一个道理。多余话不说。</p>
分享到:
评论

相关推荐

    4 第二章 第四节 水循环过程及地理意义——小学生ppt学习课件

    4 第二章 第四节 水循环过程及地理意义——小学生ppt学习课件

    《Java语言程序设计——基础篇》第四章循环示例.zip

    《Java语言程序设计——基础篇》是Java语言的经典教材,中文版分为《Java语言程序设计基础篇》和《Java语言程序设计进阶篇》主要介绍语法结构、面向对象程序设计基础知识到面向对象程序设计、图形用户界面设计、异常...

    从入门到精通HTML5——PDF——网盘链接

     第8章 层标记——div 161  教学录像:33分钟  8.1 层 162  8.1.1 层的分类 162  8.1.2 定义数据块 162  8.2 &lt;div&gt;标签 163  8.2.1 &lt;div&gt;标签的简介 163  8.2.2 &lt;div&gt;标签的属性 164  ...

    入门经典教程<<易学C++>>

    69 第8章 内存里的快捷方式——指针 84 第9章 自己设计的箱子——枚举和结构 98 第二篇 实战程序设计 第10章 高效阅读程序代码 119 第11章 调试程序代码技巧 127 第12章 编写程序技巧 150 第三篇 面向...

    跟我学spring3(1-7)

    【第二章】 IoC 之 2.1 IoC基础 ——跟我学Spring3 【第二章】 IoC 之 2.2 IoC 容器基本原理 ——跟我学Spring3 【第二章】 IoC 之 2.3 IoC的配置使用——跟我学Spring3 【第三章】 DI 之 3.1 DI的配置使用 ——跟我...

    易学C++高清完整pdf版

    第5章 有个圈儿的程序——循环语句 36 第6章 好用的“工具”——函数 51 第7章 好大的“仓库”——数组 69 第8章 内存里的快捷方式——指针 84 第9章 自己设计的箱子——枚举和结构 98 第二篇 实战程序...

    c语言配套教材电子教案

    第6章 循环控制 第7章 数组 第8章 函数 第9章 预处理命令 第10章 指针 第11章 结构体与共用体 第12章 位运算 第13章 文件 第14章 C++对C的扩充 第15章 C++的面向对象基础 第16章 常见错误和程序调试

    易学C++ 含源代码

     第5章 有个圈儿的程序——循环语句 36  第6章 好用的“工具”——函数 51  第7章 好大的“仓库”——数组 69  第8章 内存里的快捷方式——指针 84  第9章 自己设计的箱子——枚举和结构 98  ...

    易学C++[C++著名的基础书籍潘嘉杰著]

    69 第8章 内存里的快捷方式——指针 84 第9章 自己设计的箱子——枚举和结构 98 第二篇 实战程序设计 第10章 高效阅读程序代码 119 第11章 调试程序代码技巧 127 第12章 编写程序技巧 150 第三篇 面向...

    数据结构——用C描述

    第二章 线性表(参考答案) 在以下习题解答中,假定使用如下类型定义: (1)顺序存储结构: #define MAXSIZE 1024 typedef int ElemType;// 实际上,ElemType可以是任意类型 typedef struct { ElemType data...

    从零开始Android游戏编程电子书 word之Docx版

    第二章 创建第一个程序Hello Tank.docx 第三章 显示文字和图片.docx 第四章 响应用户事件.docx 第五章 小结——扫雷游戏的实现.docx 第六章 SurfaceView动画.docx 第七章 精灵、帧动画与碰撞检测.docx 第八章 ...

    C#全能速查宝典

    1.3.5 for语句——循环语句 32 1.3.6 foreach语句——枚举一个集合的元素 33 1.3.7 goto语句——跳转到标签 34 1.3.8 if…else语句——条件判断语句 36 1.3.9 return语句——返回 38 1.3.10 switch case语句——条件...

    C程序设计语言_第2版(带书签目录)

    第二章 类型、运算符与表达式 2.1 变量名 2.2 数据类型与长度 2.3 常量 2.4 声明 2.5 算术运算符 2.6 关系运算符与逻辑运算符 2.7 类型转换 2.8 自增运算符与自减运算符 2.9 按位运算符 2.10 赋值运算符与...

    Python教学反思(二).pdf

    Python教学反思(⼆) 教学反思(⼆) 以Python为载体的程序教学活动已有8节课。...本课通过创设理财产品收益过程的情境,主要教学内容是循环结构的两种⽅式:⼀、计数循环——利滚利,财越理越 多;⼆、条件

    autocad完全应用指南.autolisp+dcl+visuallisp程序设计篇(2011年4月第一版).part3.rar

    第二章autolisp的关键、基本结构与语法 第三章快速分类浏览autolisp功能函数 第四章新手上路(一)——万丈高楼平地起 第五章新手上路(二)——参数设计关键技巧 第六章对象属性的取得与活用技巧 第七章灵活掌握循环、...

    3D游戏卷2:动画与高级实时渲染技术——2

    第二部分 实时渲染 第4章 实时渲染 4.1 简介 4.2 顶点、像素和贴图 4.2.1 基本的逐像素着色 4.2.2 着色和坐标空间 4.2.3 25年来主流的插值着色方法和颜色贴图 4.2.4 标量表示 4.3 因式分解法 4.3.1 使用因式分解着色...

    3D游戏卷2:动画与高级实时渲染技术——1

    第二部分 实时渲染 第4章 实时渲染 4.1 简介 4.2 顶点、像素和贴图 4.2.1 基本的逐像素着色 4.2.2 着色和坐标空间 4.2.3 25年来主流的插值着色方法和颜色贴图 4.2.4 标量表示 4.3 因式分解法 4.3.1 使用因式分解着色...

    TensorFlow for Machine Intelligence

    第二部分(第3~4章)深入介绍TensorFlow API的基础知识和机器学习基础。第三部分(第5~6章)探讨如何用TensorFlow实现高级深度模型,涉及卷积神经网络(或CNN)模型和循环神经网络(或RNN)模型。第四部分(第7~8章...

    中文第二版模式分类

    第1章 绪论 1.1 机器感知 1.2 一个例子 1.3 模式识别系统 1.4 设计循环 1.5 学习和适应 1.6 本章小结 全书各章概要 文献和历史评述 参考文献 第2章 贝叶斯决策论 2.1 引言 2.2 贝叶斯决策论——连续特征 2.3 最小...

Global site tag (gtag.js) - Google Analytics