【C# 线程】编译器代码优化技术 循环提升:Loop Hoisting
转载自: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 寄存器
其中,rdx
和r8
寄存器分别储存着 x 和 y 的值,两者的和被储存在ebp
寄存器,以后的指令都使用这个寄存器中的值,不再重复计算。
当然,并不是所有的循环代码都可以被优化,这涉及到 Loop-invariant
条件的判定,我们下篇文章再讲。
【C# 线程】编译器代码优化技术 循环提升:Loop Hoisting的更多相关文章
- Looper.loop() android线程中的消息循环
Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Loope ...
- 简单了解一下事件循环(Event Loop)
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- 事件循环Event loop到底是什么
摘要:本文通过结合官方文档MDN和其他博客深入解析浏览器的事件循环机制,而NodeJS有另一套事件循环机制,不在本文讨论范围中.process.nextTick和setImmediate是NodeJS ...
- TMsgThread, TCommThread -- 在delphi线程中实现消息循环
http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...
- TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)
在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供. 花了两天的事件研究了 ...
- 心智与认知(1): 反馈循环(Feedback loop)
目录: ** 0x01 反馈循环(Feedback loop) | How to see System in everyday life ** 0x02 如何像视频游戏一样剖析你的人生?| 打怪升级这 ...
- JS事件循环(Event Loop)机制
前言 众所周知,为了与浏览器进行交互,Javascript是一门非阻塞单线程脚本语言. 为何单线程? 因为如果在DOM操作中,有两个线程一个添加节点,一个删除节点,浏览器并不知道以哪个为准,所以只能选 ...
- 事件循环 event loop 究竟是什么
事件循环 event loop 究竟是什么 一些概念 浏览器运行时是多进程,从任务管理器或者活动监视器上可以验证. 打开新标签页和增加一个插件都会增加一个进程,如下图:  浏览器渲染进程是多线程,包 ...
- oracle的控制语句if和循环语句loop while for
pl/sql的控制结构if-then if-then-else if-then-elsif-else 案例1:编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该雇员工资增加10% ...
随机推荐
- nodejs express异常捕获
参考链接: http://blog.coinidea.com/web开发/nodejs-1131.html 由于nodejs是非阻塞单进程单线程的,一旦nodejs抛出异常,整个服务就会停掉.服务将会 ...
- vue学习6-if判断
<p v-if="weather=='sun'">去公园</p> <p v-else-if="weather=='s'&qu ...
- JavaFx 实现按钮防抖
原文地址:JavaFx 实现按钮防抖 | Stars-One的杂货小窝 Android平台的APP,一般是有需要进行设置按钮的防抖(即在短时间内无法多次点击),我想在JavaFx项目中也是实现防抖功能 ...
- gin中XML/JSON/YAML/ProtoBuf 渲染
package main import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/testdata/p ...
- 使用ansible 批量分发ssh密钥
先看执行结果 看看ssh.yml怎么写的 看看hosts文件中backup组怎么写的
- Windows 下如何查看文件夹被哪个进程所占用
- 优化.NET 应用程序 CPU 和内存的11 个实践
https://michaelscodingspot.com/cpu-bound-memory-bound/ 优化.NET 应用程序 CPU 和内存的11 个实践 凡事都有其限度,对吧?汽车只能开这么 ...
- asyncio和aiohttp
asyncio官网 https://docs.python.org/zh-cn/3/library/asyncio-task.html 下面为伪代码: import aiohttp import as ...
- VMware网络连接模式(桥接、NAT以及仅主机模式的详细介绍和区别)
VMware 桥接模式 VMware桥接模式,也就是将虚拟机的虚拟网络适配器与主机的物理网络适配器进行交接,虚拟机中的虚拟网络适配器可通过主机中的物理网络适配器直接访问到外部网络(例如图中所示的局域网 ...
- JFrame 的层次结构 及 背景设置说明
感谢原文:https://blog.csdn.net/qq_32006373/article/details/49659129 一.JFrame 的层次结构 我们通过两个图来说明一下 JFrame 的 ...