转载自:https://gandalfliang.github.io/2019/01/15/loop-hoisting/

Loop Hoisting

上篇文章中,提到 Loop Hoisting ,这是一个常见的编译器优化项。我们总是能通过汇编代码等低级语言来“窥探”代码实际是怎么“指示”硬件运行的(这边文章不会涉及到详细的汇编内容,但是会用C#反编译后得到的汇编代码来辅助说明)。如果你看过我前面的几篇文章,会发现我用了大量反编译后的汇编代码来辅助说明,毕竟,千言不如实际的“证据”有说服力。

言归正传,Loop Hoisting,循环提升(粗略的翻译),编译器对循环代码中 loop-invariant 的代码提取出循环体外,防止循环结构内CPU对主存的重复读取。这很好理解,减少 CPU 与主存之间的 IO 次数,能有效提升程序的运行效率。观察下面的例子:

namespace loop_hoisting
{
class Program
{
static void Main(string[] args)
{
int[] array = new int[] { 1, 2, 3 };
int x = 10;
int y = 11; LoopHoistTest(array, x, y);
} static void LoopHoistTest(int[] array, int x, int y)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = x + y;
}
}
}
}

很简单的一个例子,遍历列表且赋值。LoopHoistTest 函数的循环判断里,直接读取列表的长度,编译器在碰到这种情况,会对其进行优化,将对列表长度的读取进行提升(Hoist),在循环体入口处缓存列表长度,并以此为判断依据,也就是说,从汇编代码的角度,循环判断始终去寄存器中读取缓存的列表长度信息,而不是每次都到主存中读取,以此来提到运行效率。另外,x+y很明显也是一段 loop-invariant 代码,相似地,编译器会将 x+y 的值缓存在某个通用寄存器内,并以此做赋值运算。编译器优化后的代码,就相当于:

namespace loop_hoisting
{
class Program
{
static void Main(string[] args)
{
int[] array = new int[] { 1, 2, 3 };
int x = 10;
int y = 11; LoopHoistTest(array, x, y);
} static void LoopHoistTest(int[] array, int x, int y)
{
int length = array.Length;
int sum = x+y; for (int i = 0; i < length; i++)
{
array[i] = sum;
}
}
}
}

观察汇编代码:

第一个红色框选的汇编代码:

mov   ebx,dword ptr [rsi+8] //将rsi寄存器值加上8的偏移量指向的主存中的值复制到ebx通用寄存器

其中 rsi 寄存器中的值就是主存中 array 的地址,偏移的8位指向 length 字段,这段指令将数值中的长度信息储存在 ebx 通用寄存器中,并且在以后的 cmp 指令中使用,而不是直接与主存中的内容比较。
lea   ebp,[rdx+r8] //将 rdx 和 r8 寄存器中的值相加并传送到 ebp 寄存器

其中,rdxr8 寄存器分别储存着 x 和 y 的值,两者的和被储存在 ebp 寄存器,以后的指令都使用这个寄存器中的值,不再重复计算。

当然,并不是所有的循环代码都可以被优化,这涉及到 Loop-invariant 条件的判定,我们下篇文章再讲。

【C# 线程】编译器代码优化技术 循环提升:Loop Hoisting的更多相关文章

  1. Looper.loop() android线程中的消息循环

    Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Loope ...

  2. 简单了解一下事件循环(Event Loop)

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  3. 事件循环Event loop到底是什么

    摘要:本文通过结合官方文档MDN和其他博客深入解析浏览器的事件循环机制,而NodeJS有另一套事件循环机制,不在本文讨论范围中.process.nextTick和setImmediate是NodeJS ...

  4. TMsgThread, TCommThread -- 在delphi线程中实现消息循环

    http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...

  5. TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)

    在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供.   花了两天的事件研究了 ...

  6. 心智与认知(1): 反馈循环(Feedback loop)

    目录: ** 0x01 反馈循环(Feedback loop) | How to see System in everyday life ** 0x02 如何像视频游戏一样剖析你的人生?| 打怪升级这 ...

  7. JS事件循环(Event Loop)机制

    前言 众所周知,为了与浏览器进行交互,Javascript是一门非阻塞单线程脚本语言. 为何单线程? 因为如果在DOM操作中,有两个线程一个添加节点,一个删除节点,浏览器并不知道以哪个为准,所以只能选 ...

  8. 事件循环 event loop 究竟是什么

    事件循环 event loop 究竟是什么 一些概念 浏览器运行时是多进程,从任务管理器或者活动监视器上可以验证. 打开新标签页和增加一个插件都会增加一个进程,如下图:  浏览器渲染进程是多线程,包 ...

  9. oracle的控制语句if和循环语句loop while for

    pl/sql的控制结构if-then if-then-else if-then-elsif-else 案例1:编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该雇员工资增加10% ...

随机推荐

  1. 【记录一个问题】libtask无法在android下编译通过

    源码来自:https://github.com/msteinert/libtask 首先是asm.S无法编译通过. 其次,编译context.c出现这些错误: .//context.c:124:19: ...

  2. Spring中的单例模式

    Spring中的单例模式 单例模式的介绍 1.1 简介 ​ 保证整个应用中某个实例有且只有一个 1.2作用 保证一个类仅有一个实例,并且提供一个访问它的全局访问点. 单例模式的优点和缺点 单例模式的优 ...

  3. C# 实例解释面向对象编程中的单一功能原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  4. 求Fibonacci数列通项公式

    0. Intro \[f_n=\begin{cases} 0 & (n=0) \\ 1 & (n=1) \\ f_{n-1}+f_{n-2} & (n>1) \end{c ...

  5. AtCoder AGC003 简要题解

    A 首先横向和纵向互相独立,因此只考虑横向的情况. 那么显然只要不只往一边走都一定存在一种构造方式,直接判断即可,复杂度 \(\mathcal{O}(|S|)\). B 首先相邻两个数同时配对两次可以 ...

  6. C语言中缀表达式求值(综合)

    题前需要了解的:中缀.后缀表达式是什么?(不知道你们知不知道,反正我当时不知道,搜的百度) 基本思路:先把输入的中缀表达式→后缀表达式→进行计算得出结果 栈:"先进先出,先进后出" ...

  7. 部分文件的MIMEType

    类型 文件拓展名 MIMEType 图片 png image/png bmp\dib image/bmp jpe\jpeg\jpg image/jpeg gif image/gif 多媒体 mp3 a ...

  8. linux centos6.3 升级svn1.6到svn1.8

    linux centos6.3 升级svn1.6 1. 删除svn1.6 #yum remover subverson12.设置svn1.8安装源 vim /etc/yum.repos.d/wandi ...

  9. Linux命令date日期时间和Unix时间戳互转

    A.将日期转换为Unix时间戳将当前时间以Unix时间戳表示: date +%s 输出如下: 1361542433 转换指定日期为Unix时间戳: date -d '2013-2-22 22:14' ...

  10. java基础之抽象类的介绍

    抽象类的特点: 1.当方法只有声明没有具体实现的时候,需要用abstract修饰符修饰.抽象方法必须定义在抽象类当中,所以抽象类也需要用abstract修饰 2.抽象类不可以被实例化,为什么呢?   ...