C# 为 ++ 运算符生成 IL - 前缀/后缀表示法何时以及为何更快

2024-04-02

由于这个问题是关于增量运算符和前缀/后缀表示法的速度差异,我将非常仔细地描述这个问题,以免 Eric Lippert 发现它并激怒我!

(有关我为什么要问的更多信息和更多详细信息,请访问http://www.codeproject.com/KB/cs/FastLessCSharpIteration.aspx?msg=3899456#xx3899456xx/ http://www.codeproject.com/KB/cs/FastLessCSharpIteration.aspx?msg=3899456#xx3899456xx/)

我有四个代码片段,如下所示:-

(1) 单独、前缀:

    for (var j = 0; j != jmax;) { total += intArray[j]; ++j; }

(2) 单独、后缀:

    for (var j = 0; j != jmax;) { total += intArray[j]; j++; }

(3) 索引器、后缀:

    for (var j = 0; j != jmax;) { total += intArray[j++]; }

(4) 索引器、前缀:

    for (var j = -1; j != last;) { total += intArray[++j]; } // last = jmax - 1

我试图做的是证明/反驳在此上下文中前缀和后缀表示法之间是否存在性能差异(即局部变量,因此不是易失性的,不能从另一个线程更改等),如果有,为什么会这样。

速度测试表明:

  • (1)和(2)彼此以相同的速度运行。

  • (3)和(4)彼此以相同的速度运行。

  • (3)/(4) 比 (1)/(2) 慢约 27%。

因此,我得出的结论是,选择前缀表示法相对于后缀表示法本身并没有性能优势。然而当操作结果如果实际使用了它,那么这会导致代码比简单地丢弃它要慢。

然后我使用 Reflector 查看了生成的 IL,发现了以下内容:

  • IL 字节数在所有情况下都是相同的。

  • .maxstack 在 4 和 6 之间变化,但我相信它仅用于验证目的,因此与性能无关。

  • (1) 和 (2) 生成完全相同的 IL,因此时序相同也就不足为奇了。所以我们可以忽略(1)。

  • (3) 和 (4) 生成非常相似的代码 - 唯一相关的区别是 dup 操作码的定位以解释操作结果。同样,时间相同也就不足为奇了。

因此,我随后比较了 (2) 和 (3),以找出导致速度差异的原因:

  • (2) 使用 ldloc.0 操作两次(一次作为索引器的一部分,然后作为增量的一部分)。

  • (3) 使用了 ldloc.0,紧接着是一个 dup 操作。

因此,(1)(和(2))中 j 递增的相关 IL 为:

// ldloc.0 already used once for the indexer operation higher up
ldloc.0
ldc.i4.1
add
stloc.0

(3) 看起来像这样:

ldloc.0
dup // j on the stack for the *Result of the Operation*
ldc.i4.1
add
stloc.0

(4) 看起来像这样:

ldloc.0
ldc.i4.1
add
dup // j + 1 on the stack for the *Result of the Operation*
stloc.0

现在(最后!)回答这个问题:

(2) 更快,因为 JIT 编译器识别以下模式ldloc.0/ldc.i4.1/add/stloc.0只是将局部变量加 1 并对其进行优化? (并且存在一个dup(3) 和 (4) 打破了该模式,因此错过了优化)

并补充一下: 如果这是真的,那么至少对于(3)来说,不会取代dup和另外一个ldloc.0重新引入该模式?


好吧,经过大量研究(我知道很难过!),我想已经回答了我自己的问题:

答案是也许。 显然,JIT 编译器确实会寻找模式(请参阅http://blogs.msdn.com/b/clrcode Generation/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx)来决定何时以及如何优化数组边界检查,但我不知道它是否与我猜测的模式相同。

在这种情况下,这是一个有争议的问题,因为(2)的相对速度增加的原因不止于此。事实证明,x64 JIT 编译器足够聪明,可以计算出数组长度是否恒定(并且似乎也是循环中展开次数的倍数):因此,代码仅在每次迭代结束时进行边界检查,并且每次展开都变成:-

        total += intArray[j]; j++;
00000081 8B 44 0B 10          mov         eax,dword ptr [rbx+rcx+10h] 
00000085 03 F0                add         esi,eax 

我通过更改应用程序以在命令行上指定数组大小并查看不同的汇编器输出来证明了这一点。

在这次练习中发现的其他事情:-

  • 对于独立的增量操作(即不使用结果),前缀/后缀之间的速度没有差异。
  • 当在索引器中使用增量操作时,汇编器显示前缀表示法稍微更有效(并且在原始情况下非常接近,我认为这只是时间差异并将它们称为相等 - 我的错误)。当编译为 x86 时,差异更加明显。
  • 循环展开确实有效。与具有数组边界优化的标准循环相比,4 次汇总始终提供 10%-20% 的改进(x64/常量情况为 34%)。增加汇总数量会带来不同的计时,在索引器中有后缀的情况下,有些时间会慢得多,因此如果展开,我将坚持使用 4,并且仅在特定情况的大量计时后才更改它。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 为 ++ 运算符生成 IL - 前缀/后缀表示法何时以及为何更快 的相关文章

  • 添加对共享类的多个 WCF 服务的服务引用

    我正在尝试将我的 WCF Web 服务拆分为几个服务 而不是一个巨大的服务 但是 Visual Studio Silverlight 客户端 复制了两个服务共享的公共类 这是一个简单的例子来说明我的问题 在此示例中 有两个服务 两者都返回类
  • 在 OpenCL 中将函数作为参数传递

    是否可以在 OpenCL 1 2 中将函数指针传递给内核 我知道可以用C实现 但不知道如何在OpenCL的C中实现 编辑 我想做这篇文章中描述的同样的事情 在 C 中如何将函数作为参数传递 https stackoverflow com q
  • 处理 fanart.tv Web 服务响应 JSON 和 C#

    我正在尝试使用 fanart tv Webservice API 但有几个问题 我正在使用 Json Net Newtonsoft Json 并通过其他 Web 服务将 JSON 响应直接反序列化为 C 对象 这里的问题是元素名称正在更改
  • TextBox 焦点的 WinForms 事件?

    我想添加一个偶数TextBox当它有焦点时 我知道我可以用一个简单的方法来做到这一点textbox1 Focus并检查布尔值 但我不想那样做 我想这样做 this tGID Focus new System EventHandler thi
  • ZLIB 解压缩

    我编写了一个小型应用程序 该应用程序应该解压缩以 gzip deflate 格式编码的数据 为了实现这一点 我使用 ZLIB 库 使用解压缩功能 问题是这个功能不起作用 换句话说 数据不是未压缩的 我在这里发布代码 int decompre
  • 禁用 LINQ 上下文的所有延迟加载或强制预先加载

    我有一个文档生成器 目前包含约 200 个项目的查询 但完成后可能会超过 500 个 我最近注意到一些映射表示延迟加载 这给文档生成器带来了一个问题 因为它需要根据生成的文档来访问所有这些属性 虽然我知道DataLoadOptions可以指
  • 两组点之间的最佳匹配

    I ve got two lists of points let s call them L1 P1 x1 y1 Pn xn yn and L2 P 1 x 1 y 1 P n x n y n 我的任务是找到它们点之间的最佳匹配 以最小化它
  • 通过不同 DLL 或 EXE 中的指针或引用访问 STL 对象时发生访问冲突

    我在使用旧版 VC6 时遇到以下问题 我只是无法切换到现代编译器 因为我正在处理遗留代码库 http support microsoft com kb 172396 http support microsoft com kb 172396
  • 用于从字符串安全转换的辅助函数

    回到 VB6 我编写了一些函数 让我在编码时无需关心字符串的 null 和 数字的 null 和 0 等之间的区别 编码时 没有什么比添加特殊情况更能降低我的工作效率了用于处理可能导致一些不相关错误的数据的代码 9999 10000 如果我
  • 从匿名类型获取值

    我有一个方法如下 public void MyMethod object obj implement 我这样称呼它 MyMethod new myparam waoww 那么我该如何实施MyMethod 获取 myparam 值 Edit
  • 过期时自动重新填充缓存

    我当前缓存方法调用的结果 缓存代码遵循标准模式 如果存在 则使用缓存中的项目 否则计算结果 在返回之前将其缓存以供将来调用 我想保护客户端代码免受缓存未命中的影响 例如 当项目过期时 我正在考虑生成一个线程来等待缓存对象的生命周期 然后运行
  • 运行代码首先迁移更新数据库时出错

    我在迁移到数据库时遇到问题 并且似乎找不到我遇到的错误的答案 System MissingMethodException Method not found System Data Entity Migrations Builders Tab
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因
  • 我应该在应用程序退出之前运行 Dispose 吗?

    我应该在应用程序退出之前运行 Dispose 吗 例如 我创建了许多对象 其中一些对象具有事件订阅 var myObject new MyClass myObject OnEvent OnEventHandle 例如 在我的工作中 我应该使
  • 过度使用委托对性能来说是一个坏主意吗? [复制]

    这个问题在这里已经有答案了 考虑以下代码 if IsDebuggingEnabled instance Log GetDetailedDebugInfo GetDetailedDebugInfo 可能是一个昂贵的方法 因此我们只想在调试模式
  • 如何查明CONFIG_FANOTIFY_ACCESS_PERMISSIONS是否启用?

    我想利用fanotify 7 http man7 org linux man pages man7 fanotify 7 html我遇到的问题是在某些内核上CONFIG FANOTIFY ACCESS PERMISSIONS不起作用 虽然C
  • 为什么 Ajax.BeginForm 在 Chrome 中不起作用?

    我正在使用 c NET MVC2 并尝试创建一个 ajax 表单来调用删除数据库记录 RemoveRelation 的方法 删除记录的过程正在按预期进行 删除记录后 表单应调用一个 JavaScript 函数 从视觉效果中删除该记录 Rem
  • 以编程方式使用自定义元素创建网格

    我正在尝试以编程方式创建一个网格 并将自定义控件作为子项附加到网格中 作为 2x2 矩阵中的第 0 行第 0 列 为了让事情变得更棘手 我使用了 MVVM 设计模式 下面是一些代码可以帮助大家理解这个想法 应用程序 xaml cs base
  • boost::program_options:带有固定和可变标记的参数?

    是否可以在 boost program options 中使用此类参数 program p1 123 p2 234 p3 345 p12 678 即 是否可以使用第一个标记指定参数名称 例如 p 后跟一个数字 是动态的吗 我想避免这种情况
  • 文件修改时间检查的成本

    对于Linux下包含少量字节的文件 我只需要处理自上次处理以来发生更改的时间 我通过调用 PHP 检查文件是否被更改clearstatcache filemtime 定期 由于整个文件总是很小 因此删除对 filemtime 的调用并通过将

随机推荐

  • 我应该在fragment中哪里调用Rest API

    我正在使用带有 3 个片段的底部导航 在 Home fragment 上 我请求 API 获取数据并在回收器视图中显示 我的问题是每当我切换片段并再次回到 Home fragment 时 它都会重新创建布局 并再次从 API 获取数据 我只
  • console.log 内具有控制台日志输出的函数打印 undefined

    function haha console log haha console log haha Prints haha undefined 是因为如果你没有在函数中指定 return 它将返回 undefined 这就是第二个 consol
  • 表达式解析为未使用的属性

    override func prepare for segue UIStoryboardSegue sender Any if segue identifier toDetails if let indexPath sender as In
  • 从 CDN JS 导入 firebase firestore 不起作用

    我正在从 CDN 导入 Firebase Firestore 以在本地服务器上运行 我按照文档所述将其导入 就在这里 https firebase google com docs web alt setup https firebase g
  • 我应该如何使用 Hibernate 从 JPQL 查询引用内部枚举(在实体内定义)?

    我有一个实体类如下 package stuff Entity class Thing Id GeneratedValue private Long id Basic Enumerated private State state public
  • 如果不存在则创建文件夹路径(从 VBA 保存)

    我在工作表中有一个项目列表 如下所示 我的代码遍历每一行并对供应商进行分组 并将一些信息复制到每个供应商的工作簿中 在此场景中 有 2 个唯一的供应商 因此将创建 2 个工作簿 这有效 接下来我想将每个工作簿保存在特定的文件夹路径中 如果文
  • 带有 nginx 的网络服务器一直工作到创建 .save 文件

    在过去 48 小时内到处进行谷歌搜索以找出为什么我的服务器突然停止工作后 最后 我自己找到了答案 我决定将其发布在这里 供那些在整个周末都在谷歌上搜索如此琐碎事情的人使用 在 nginx 上加载了所有内容 LEMP 工作完美 但后来我尝试进
  • Excel 中的数据整理 - 重新排列列和行

    我有一个巨大的 Excel 数据集 我想重新排列行和列 这是数据的片段 它看起来是什么样子以及我希望它看起来是什么样子 Area Channel Unit Year1 Year2 Year3 Year4 bel dc share 25 36
  • 使用 java11 构建和部署 javafx 应用程序

    我按照以下步骤操作https blog jetbrains com idea 2013 03 packaging javafx 2 applications in intellij idea 121 https blog jetbrains
  • 为什么 SQL Server SET DEADLOCK_PRIORITY HIGH 不被遵守?

    我捕获了 SQL Server 2012 死锁图 使用盖尔 肖的 https www red gate com simple talk sql performance sql server deadlocks by example 查询 显
  • MongoDB C# 连接/断开(官方驱动程序)

    请告诉我如何通过官方 C 驱动程序连接 断开到 MongoDB 问题很简单 乍一看问题很微不足道 但是 1 我是否需要自己调用Disconnect方法 或者它会被诸如Dispose之类的方法关闭 2 每次需要向Mongo发出请求时都需要连接
  • statsmodel 预测开始和结束索引

    我正在尝试实现 statsmodel 包中的预测功能 prediction results predict start 1 end len test exog test 输入 测试和输出预测的日期不一致 前者为2012年1月4日至2012年
  • getter 和 setter 的 Google 样式指南属性

    我对其中的一项建议感到好奇有关属性的 Google Python 风格指南 https google github io styleguide pyguide html Properties 他们在其中给出了以下示例 class Squar
  • SQLite 与 Android NDK

    是否可以在 Android 手机上将 SQLite 与 C 结合使用 我还没有找到任何关于如何实现这一点的文档 只需从以下位置下载 SQLite3 合并源文件 http www sqlite org download html http w
  • 如何将 Xcode 项目转换为使用 ARC(自动引用计数)?

    当我运行 Convert to Obj C 时 它首先运行预检查 我希望预检查忽略一些文件 并且我认为可以在这些文件上设置编译器标志 fno objc arc 但预检查阶段似乎忽略了该标志 并重置了它 所以我真的不知道如何超越预检查 预检查
  • 按名称调用 Django celery 任务

    我需要从models py调用一个celery任务 在tasks py中 唯一的问题是 tasks py导入models py 所以我无法从models py导入tasks py 有没有什么方法可以仅使用名称来调用 celery 任务 而无
  • 将多个数组作为参数传递给 Bash 脚本?

    我看过 但只看到了脚本中传递的一个数组的答案 我想将多个数组传递给 bash 脚本 该脚本将它们分配为单独的变量 如下所示 myScript sh array1 array2 array3 这样 var1 array1 and var2 a
  • Java 和 C++ 中的简单变量

    我在一些资料中看到这样一句话 在 Java 中 简单数据类型 例如 int 和 char 的运行方式与 C 中一样 我想知道 Java 和 C 中它们实际上是不同的 在C 中 像Java中的基元这样的简单变量也被分配了一个内存地址 因此C
  • 使用 SQL 获取每小时统计信息

    我们有一张桌子 名字 员工注册 有字段 employeeNo employeeName Registered on Here 注册日期是一个时间戳 我们要求在几天内按小时进行注册 例如 08 年 1 月 1 日 12 点 下午 1 点 15
  • C# 为 ++ 运算符生成 IL - 前缀/后缀表示法何时以及为何更快

    由于这个问题是关于增量运算符和前缀 后缀表示法的速度差异 我将非常仔细地描述这个问题 以免 Eric Lippert 发现它并激怒我 有关我为什么要问的更多信息和更多详细信息 请访问http www codeproject com KB c