• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C语言中对于循环结构优化的一些入门级方法简介

C语言中对于循环结构优化的一些入门级方法简介

作者:lisperl 字体:[增加 减小] 来源:互联网 时间:2017-05-28

lisperl 通过本文主要向大家介绍了c语言程序优化,c语言优化,c语言代码优化,c语言界面优化,c语言性能优化等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

一.代码移动

将在循环里面多次计算,但是结果不会改变的计算,移到循环外面去。

例子:

优化前:

void lower1(char *s){
int i;
for(i=0;i<strlen(s);++i)
   if(s[i]>='A'&&s[i]<='Z')
    s[i]-=('A'-'a');
}
优化后:
void lower2(char *s){
int i;
int len=strlen(s);
for(int i=0;i<len;++i)
  if(s[i]>='A'&&s[i]<='Z')
    s[i]-=('A'-'a');
}
</div>

优化前的版本,由于每次循环都要调用strlen计算s的长度,实际上的复杂度成了O(n2)了,而优化后的版本只需计算一次s的长度,因此性能上比优化前版本要好。

二.减少函数调用

例子:

优化前:

void sum1(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
*dest=0;
for(i=0;i<len;++i){
  data_t val;
  get_vec_element(v,i,&val);
  *dest+=val;
}
}
</div>

优化后:

data_t get_vec_start(vec_ptr v){
return v->data;
}

void sum2(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
data_t *data=get_vec_start(v);
*dest=0;
for(i=0;i<len;++i)
  *dest+=data[i];
}

</div>

优化前的版本在每次循环中都要调用一次get_vec_element获得相应的项,而优化后的版本只需在循环外调用一次get_vec_start获得开始的内存地址,循环内直接访问内存,无需调用函数。

三.减少内存访问

例子:

优化前:

void sum2(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
data_t *data=get_vec_start(v);
*dest=0;
for(i=0;i<len;++i)
  *dest+=data[i];
}
</div>

优化后:

void sum3(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
data_t *data=get_vec_start(v);
data_t acc=0;
for(i=0;i<len;++i)
  acc+=data[i];
*dest=acc;
}
</div>

优化前的版本每次迭代都要从dest读出值再加上data[i],再将结果写回dest。这样的读写很浪费,因此每次迭代开始从dest读出的值就是上次迭代写回dest的指。优化后的版本通过加入acc临时变量,它循环中累积计算出的结果,循环结束后再写回。

这里给出两个版本相应的汇编结果就可以很清楚看出区别:

优化前:

2015122102845375.png (704×221)

优化前的版本每次迭代都要从dest读出值再加上data[i],再将结果写回dest。这样的读写很浪费,因此每次迭代开始从dest读出的值就是上次迭代写回dest的指。优化后的版本通过加入acc临时变量,它循环中累积计算出的结果,循环结束后再写回。

第二行和第四行分别对dest进行了读写。

优化后:

2015122102904746.png (677×180)

从汇编结果可以看出编译器将acc直接放在了寄存器里,循环中无需对内存进行读写。

四.循环展开

循环展开可以减少循环的次数,对程序的性能带了两方面的提高。一是减少了对循环没有直接贡献的计算,比如循环计数变量的计算,分支跳转指令的执行等。二是提供了进一步利用机器特性进行的优化的机会。

例子:

优化前的代码见前一篇博客里的sum3.

优化后:

void sum4(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
int limit=len-3;
data_t *data=get_vec_start(v);
data_t acc=0;
for(i=0;i<limit;i+=4){
  acc=acc+data[i]+data[i+1];
  acc=acc+data[i+2]+data[i+3];
}
for(;i<len;++i)
  acc+=data[i];

*dest=acc;
}

</div>

通过循环展开,每次迭代将累加4个元素,减少了循环次数,从而减少了总的执行时间(单独使用这种优化方法,对浮点数累乘几乎没有提高,但是整数累乘得益于编译器的重关联代码变化会有大幅度提高)。

这种优化可以直接利用编译器完成,将优化level设定到较高,编译器会自动进行循环展开。使用gcc,可以显式使用-funroll-loops选项。

五.提高并行性

现代处理器大多采用了流水线、超标量等技术,可以实现指令级并行。我们可以利用这个特性对代码做进一步的优化。

2.1使用多个累积变量

优化代码示例

void sum5(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
int limit=len-1;
data_t *data=get_vec_start(v);
data_t acc0=0;
data_t acc1=0;
for(i=0;i<limit;i+=2){
  acc0+=data[i];
  acc1+=data[i+1];
}
for(;i<len;++i)
  acc0+=data[i];

*dest=acc0+acc1;
}

</div>

这里同时使用了循环展开和使用多个累加变量,一方面减少了循环次数,另一方面指令级并行的特性使得每次迭代的两次加法可以并行执行。基于这两点可以显著减少程序执行的时间。通过增加展开的次数和累加变量的个数,可以进一步提高程序的性能,直到机器指令执行的吞吐量的极限。

2.2重结合变换

除了使用多个累积变量显式利用机器的指令级并行特性外,还可以对运算重新结合变换,打破顺序相关性来享受指令级并行带来的好处。

在sum4中,acc=acc+data[i]+data[i+1]的结合顺序是acc=(acc+data[i])+data[i+1];

我们将之变成acc=acc+(data[i]+data[i+1]);

代码如下:

void sum6(vec_ptr v,data_t *dest){
int i;
int len=vec_length(v);
int limit=len-3;
data_t *data=get_vec_start(v);
data_t acc=0;
for(i=0;i<limit;i+=4){
  acc=acc+(data[i]+data[i+1]);
  acc=acc+(data[i+2]+data[i+3]);
}
for(;i<len;++i)
  acc+=data[i];

*dest=acc;
}

</div>

进一步增加循环展开的次数,可以进一步提高程序性能,最终也可以达到机器指令执行的吞吐量的极限。(在循环展示提到的整数乘法的性能提高就在于编译器隐式采取了这种变换,但是由于浮点数不具备结合性,所以编译器没有采用,但是程序员在保证程序结果正确性的情况下,可以显式使用这一点)。

</div>
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C语言中对于循环结构优化的一些入门级方法简介

相关文章

  • 2017-05-28浅析C++中前置声明的应用与陷阱
  • 2017-05-28哈夫曼算法构造代码
  • 2017-05-28C语言演示对归并排序算法的优化实现
  • 2017-05-28C++事件处理中的__hook与__unhook用法详解
  • 2017-05-28使用单链表实现多项式计算示例
  • 2017-05-28DSP中浮点转定点运算--浮点数的存储格式
  • 2017-05-28关于vector迭代器失效的几种情况总结
  • 2017-05-28剖析C++编程当中指针作为函数参数的用法
  • 2017-05-28实例讲解在C++的函数中变量参数及默认参数的使用
  • 2017-05-28c# 实现获取汉字十六进制Unicode编码字符串的实例

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C++指向类成员函数的指针详细解析
    • C语言中free函数的使用详解
    • C++内核对象封装单实例启动程序的类
    • 平衡二叉树AVL操作模板
    • 编译错误error: stray ‘\343’in program的解决方法
    • C++调用C#的DLL实现方法
    • C++基于控制台实现的贪吃蛇小游戏
    • 浅析C/C++中动态链接库的创建和调用
    • C++堆排序算法的实现方法
    • C语言程序中递归算法的使用实例教程

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有