- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想知道为什么这个 C# 代码
long b = 20;
编译为
ldc.i4.s 0x14
conv.i8
(因为它需要 3 个字节,而不是 ldc.i8 20
所需的 9 个字节。有关详细信息,请参阅 this。)
这段代码
double a = 20;
编译成9字节指令
ldc.r8 20
而不是这个 3 字节序列
ldc.i4.s 0x14
conv.r8
(使用单声道 4.8。)
这是错失的机会还是 conv.i8
的成本超过了代码大小的增益?
最佳答案
因为 float 不是更小的 double ,整数也不是 float (反之亦然)。
所有 int
值都与 long
值具有 1:1 映射。对于 float
和 double
来说,情况并非如此 - 浮点运算在这方面很棘手。更不用说 int-float 转换不是免费的——不像将 1 字节的值压入堆栈/寄存器;查看两种方法生成的 x86-64 代码,而不仅仅是 IL 代码。 IL 代码的大小不是优化时要考虑的唯一因素。
这与 decimal
相反,它实际上是一个以 10 为底的十进制数,而不是一个以 2 为底的十进制 float 。 20M
完美映射到 20
,反之亦然,因此编译器可以自由地发出:
IL_0000: ldc.i4.s 0A
IL_0002: newobj System.Decimal..ctor
对于二进制 float ,同样的方法根本不安全(或便宜!)。
您可能认为这两种方法一定是安全的,因为我们是否在编译时将整数文字(“字符串”)转换为 double 值并不重要,或者我们是否这样做并不重要在伊利诺伊州。但事实并非如此,因为一些规范潜水揭示了:
ECMA CLR 规范,III.1.1.1:
Storage locations for floating-point numbers (statics, array elements, and fields of classes) are of fixed size. The supported storage sizes are float32 and float64. Everywhere else (on the evaluation stack, as arguments, as return types, and as local variables) floating-point numbers are represented using an internal floating-point type. In each such instance, the nominal type of the variable or expression is either float32 or float64, but its value might be represented internally with additional range and/or precision.
为简短起见,假设 float64 实际上使用 4 个二进制数字,而实现定义的浮点类型 (F) 使用 5 个二进制数字。我们想要转换一个恰好具有超过四位二进制表示的整数文字。现在比较一下它的行为:
ldc.r8 0.1011E2 ; expanded to 0.10110E2
ldc.r8 0.1E2
mul ; 0.10110E2 * 0.10000E2 == 0.10110E3
conv.r8
转换为 F,而不是 float64。所以我们实际上得到:
ldc.i4.s theSameLiteral
conv.r8 ; converted to 0.10111E2
mul ; 0.10111E2 * 0.10000E2 == 0.10111E3
糟糕 :)
现在,我很确定在任何合理的平台上,0-255 范围内的整数都不会发生这种情况。但是由于我们是根据 CLR 规范进行编码的,所以我们不能做出这样的假设。 JIT 编译器可以,但为时已晚。语言编译器可能将两者定义为等效的,但 C# 规范没有定义 - double
本地被视为 float64,而不是 F。如果您愿意,可以创建自己的语言。
无论如何,IL 生成器并没有真正优化太多。这大部分留给了 JIT 编译。如果你想要一个优化的 C#-IL 编译器,写一个——我怀疑是否有足够的好处来保证付出努力,特别是如果你的唯一目标是使 IL 代码更小。大多数 IL 二进制文件已经比等效的 native 代码小很多。
至于实际运行的代码,在我的机器上,两种方法都产生完全相同的 x86-64 程序集——从数据段加载 double 值。 JIT 可以很容易地进行这种优化,因为它知道代码实际运行在什么架构上。
关于c# - 为什么编译器优化 ldc.i8 而不是 ldc.r8?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41058165/
我想知道为什么这个 C# 代码 long b = 20; 编译为 ldc.i4.s 0x14 conv.i8 (因为它需要 3 个字节,而不是 ldc.i8 20 所需的 9 个字节。有关详细信息,请
我在研究 C# 的中间语言 (IL) 时遇到了以下代码:- //Add.il //Add Two Numbers .assembly extern mscorlib {} .assembly Add
CIL中有ldc.i4.0、ldc.i4.1、ldc.i4.2等加载指令, ldc.i4.3 ... 我想知道,是否可以使用 ldc.i4 1 而不是 ldc.i4.1 或 ldc.i4 5 而不是l
我正在尝试找出一些 CIL 代码。但这似乎这两个陈述做了同样的事情(根据我读过的所有内容)。 ldc.i4 33 和 ldc.i4.33 两者都被认为“将 int32 加载到值 33 的堆栈上”。 这
这是我的 C# 代码: const long pSize = 20; 无论我使用 x64 还是任何 CPU 在 Release模式下构建它,我都会得到以下 MSIL 指令: IL_0010: l
我是 java 字节码世界的新手。我有一些涉及字节码的调试任务。在经历那件事时,我注意到一些看起来可疑的值,但我不确定。这是完整字节码的一部分 // access flags 0x100A pri
ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , whic
不同 D 编译器的优缺点是什么?性能和标准合规性/D2 支持如何?调试器的支持程度如何?错误消息有多好以及 IDE 集成如何? 64 位支持有多好?到目前为止我的想法: DMD 成熟且维护良好 只有一
我在哪里可以找到关于 GDC(GNU D 编译器)的规范以及如何重写标准 D 类型,如 uint 等? 有关信息:我对将 D 用于内核和其他低级内容很感兴趣。 谢谢。 最佳答案 您可以遵循 D ABI
我在哪里可以找到关于 GDC(GNU D 编译器)的规范以及如何重写标准 D 类型,如 uint 等? 有关信息:我对将 D 用于内核和其他低级内容很感兴趣。 谢谢。 最佳答案 您可以遵循 D ABI
我对任何形式的 C 编程都是新手。 我有一个带有液晶屏(矩阵轨道LK204-7T-1U-USB-WB)的PLC(Teso PC1620)(使用rabbit处理器)。我正在尝试使用 LCD 内置的水平条
我正在用 ASM 编写一个程序,它使用 Tree API 将字节码添加到某些方法中。我已经使用 ASMifier 生成创建特定方法所需的代码,但我在使用以下行时遇到了一些问题: mv.visitLdc
我是一名优秀的程序员,十分优秀!