性能 - 如果有的话,循环展开仍然有用吗?

我一直试图通过循环展开来优化一些极其性能关键的代码(一种快速排序算法,在蒙特卡罗模拟中被称为数百万次)。 这是我试图加速的内循环:

// Search for elements to swap.

while(myArray[++index1] < pivot) {}

while(pivot < myArray[--index2]) {}

我尝试展开类似的东西:

while(true) {

if(myArray[++index1] < pivot) break;

if(myArray[++index1] < pivot) break;

// More unrolling

}

while(true) {

if(pivot < myArray[--index2]) break;

if(pivot < myArray[--index2]) break;

// More unrolling

}

这完全没有区别所以我把它改成了更易读的形式。 我曾经尝试过循环展开,但我有类似的经历。 鉴于现代硬件上的分支预测器的质量,何时(如果有的话)循环展开仍然是一个有用的优化?

9个解决方案

101 votes

如果你可以打破依赖链,循环展开是有意义的。 这使得无序或超标量CPU可以更好地安排事情并因此运行得更快。

一个简单的例子:

for (int i=0; i

{

sum += data[i];

}

这里参数的依赖链非常短。 如果因为数据阵列上有缓存未命中而导致停顿,那么cpu除了等待之外什么也做不了。

另一方面这段代码:

for (int i=0; i

{

sum1 += data[i+0];

sum2 += data[i+1];

sum3 += data[i+2];

sum4 += data[i+3];

}

sum = sum1 + sum2 + sum3 + sum4;

可以跑得更快。 如果在一次计算中遇到缓存未命中或其他停顿,则仍有三个其他依赖链不依赖于停顿。 乱序CPU可以执行这些。

Nils Pipenbrinck answered 2019-04-08T19:08:35Z

20 votes

那些没有任何区别,因为你正在进行相同数量的比较。 这是一个更好的例子。 代替:

for (int i=0; i<200; i++) {

doStuff();

}

写:

for (int i=0; i<50; i++) {

doStuff();

doStuff();

doStuff();

doStuff();

}

即便如此,它几乎肯定无关紧要,但你现在正在做50次比较而不是200次(想象一下比较更复杂)。

然而,手动循环展开通常是历史的工件。 这是一个很好的编译器会在重要的时候为你做的事情中的另一个。 例如,大多数人都懒得写x <<= 1或x += x而不是x *= 2.您只需编写x *= 2并且编译器将为您优化它以获得最佳效果。

基本上,对猜测编译器的猜测越来越少。

cletus answered 2019-04-08T19:09:28Z

13 votes

无论现代硬件上的分支预测如何,大多数编译器都会为您循环展开。

值得了解一下编译器为您做了多少优化。

我发现Felix von Leitner的演讲在这个问题上非常有启发性。 我建议你阅读它。 简介:现代编译器非常聪明,因此手动优化几乎从未有效。

Peter Alexander answered 2019-04-08T19:10:12Z

2 votes

据我所知,现代编译器已经在适当的情况下展开循环 - 一个例子是gcc,如果传递优化标记,那么手册说它会:

展开其编号为的循环   迭代可以确定   编译时或进入   环。

所以,在实践中,你的编译器很可能会为你做一些简单的案例。 因此,您需要确保尽可能多的循环对于编译器来说很容易确定需要多少次迭代。

Rich Bradshaw answered 2019-04-08T19:10:51Z

2 votes

无论是手动展开还是编译器展开,循环展开通常都会适得其反,特别是对于最新的x86 CPU(Core 2,Core i7)。 结论:在您计划部署此代码的任何CPU上,使用和不使用循环展开您的代码。

Paul R answered 2019-04-08T19:11:16Z

1 votes

不知道的尝试不是这样做的方法。

这种类型占总体时间的比例很高吗?

所有循环展开都会减少递增/递减的循环开销,比较停止条件和跳转。 如果你在循环中所做的事情比循环开销本身需要更多的指令周期,那么你不会在百分比方面看到太多改进。

以下是如何获得最佳性能的示例。

Mike Dunlavey answered 2019-04-08T19:12:24Z

1 votes

循环展开在特定情况下可能会有所帮助。 唯一的好处是不会跳过一些测试!

例如,它可以允许标量替换,有效插入软件预取......你会惊讶地发现它有多么有用(通过积极展开,你可以轻松地在大多数循环中获得10%的加速,即使使用-O3)。

如前所述,它在很大程度上取决于循环,编译器和实验是必要的。 制定规则很难(或者展开的编译器启发式是完美的)

Kamchatka answered 2019-04-08T19:13:03Z

0 votes

循环展开完全取决于您的问题大小。 它完全取决于您的算法能够将大小缩小为较小的工作组。 你上面做的不是那样的。 我不确定monte carlo模拟是否可以展开。

循环展开的好方案是旋转图像。 因为您可以旋转单独的工作组。 要使其工作,您必须减少迭代次数。

jwendl answered 2019-04-08T19:13:35Z

0 votes

如果循环中和循环中存在大量局部变量,则循环展开仍然有用。 要重用这些寄存器而不是为循环索引保存一个。

在您的示例中,您使用少量局部变量,而不是过度使用寄存器。

如果比较很重(即非指令),则比较(到循环结束)也是一个主要缺点,特别是如果它依赖于外部函数。

循环展开也有助于提高CPU对分支预测的意识,但无论如何都会发生这种情况。

LiraNuna answered 2019-04-08T19:14:28Z

现在的编译器还需要手动展开循环吗_性能 - 如果有的话,循环展开仍然有用吗?...相关推荐

  1. 现在的编译器还需要手动展开循环吗_一例 Go 编译器代码优化 bug 定位和修复解析...

    缘起 某日,一位友人在群里招呼我,"看到有人给 Go 提了个编译器的 bug,挺有意思,感觉还挺严重的,要不要来看看?"于是我打开了 issue 40367 .彼时,最新一条评论是 ...

  2. 现在的编译器还需要手动展开循环吗_DSP(知识点+思考题)

    DSP复习要点 第一章绪论 1.数的定标:Qn表示. 例如:16进制数2000H=8192,用Q0表示16进制数2000H=0.25,用Q15表示 2.?C54x小数的表示方法:采用2的补码小数:.w ...

  3. Android实现Banner界面广告图片循环轮播(包括实现手动滑动循环)

    前言:经常会看到有一些app的banner界面可以实现循环播放多个广告图片和手动滑动循环.本以为单纯的ViewPager就可以实现这些功能.但是蛋疼的事情来了,ViewPager并不支持循环翻页.所以 ...

  4. 都9012年了,你还在手动部署代码吗(二)

    前言 在写完基于 Webhooks 的"第一篇<都9012年了,你还在手动部署代码吗>"之后,有同学评论到"至少你得用个 docker 啊"&quo ...

  5. excel修改列名_听说你还在手动合并Excel,看看这个吧!?

    Excel合并的应用场景 工作中,常常遇到将多个Excel进行合并的任务.例如,将各位参会人员的报名表合并成一张总的参会人员表,或是将不同客户的需求明细合并为一种总表. 常规的做法是新建一个空白的Ex ...

  6. 【Linux】gcc编译器下载与手动安装

    由于 Linux 操作系统的自由.开源,在其基础上衍生出了很多不同的 Linux 操作系统,如 CentOS.Ubuntu.Debian 等.这些 Linux 发行版中,大多数都默认装有 GCC 编译 ...

  7. react 展开收起写法(手动展开收起 和 自动展开收起)

    一:手动展开收起 html写法: <div className="pack-unfold">{isShow.includes(item.chapterId)?<s ...

  8. 还在手动启动neo4j?快来使用bat批处理自动启动neo4j,之后也不用配置环境了(社区版)

    还在手动启动neo4j?快来使用bat批处理自动启动,之后不用配置环境了(社区版) 1 start_neo4j.bat 总共创建两个bat一个 开启 start,一个关闭 stop [1]在neo4j ...

  9. return的用法是什么?若用在for循环中,还会执行下一次循环吗?

    这里是修真院前端小课堂,每篇分享文从 [背景介绍][知识剖析][常见问题][解决方案][编码实战][扩展思考][更多讨论][参考文献] 八个方面深度解析前端知识/技能,本篇分享的是: 这里是修真院前端 ...

最新文章

  1. python爬网页源码_python爬虫爬取网页的内容和网页源码不同?
  2. java和python都要掌握_如果两者都决定要学的话,先学Java还是Python?
  3. python中if else语句用法_Python中if-else语句的多种写法
  4. CSS定位总结:position=static/relative/absolute/fixed时的区别、top/bottom/left/right与margin外边距的运用
  5. 新年第一笔收入:支付宝开奖,你分了多少?
  6. Linux的用户和组
  7. 如何让Mac电脑在Finder窗口顶部显示文件路径?
  8. Rust :CC编译
  9. 自我总结3dmaxs建筑场景展uv步骤
  10. no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature
  11. 人人都在谈的 “数据驱动” 到底是什么?你确认自己做的是数据驱动吗?
  12. 植物摄影——顶尖高手传授独门秘技
  13. a到z的ascii码值是多少_c语言 ASCLL码中 A~Z和a~z是多少
  14. C++ 网络编程下的socket编程(TCP\UDP),连接下位机
  15. 【转】如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源
  16. Chrome插件 Tamper Dev
  17. Gtest 测试指导 入门基础(A)
  18. 必然与偶然,本质与细节
  19. 3级网络技术第一套题
  20. 博途v15模拟量转换_S7-1200PLC中的模拟量转换

热门文章

  1. 客快物流大数据项目(八):Docker的安装和启动
  2. 2021年大数据HBase(八):Apache Phoenix的基本介绍
  3. Collections.addAll() 的使用 以及和list.addAll() 的区别
  4. Android 金钱计算BigDecimal 的使用
  5. JAVA中priorityqueue详解
  6. Redis系列2- C#中使用Redis的示例
  7. Java fork join ForkJoinPool 用法例子
  8. 练习用基础SQL语句
  9. 文本输入框、密码输入框
  10. SQL Server 日期和时间相关的数据类型有两种