gpt4 book ai didi

c# - 为什么编译器优化 ldc.i8 而不是 ldc.r8?

转载 作者:太空狗 更新时间:2023-10-29 17:47:44 26 4
gpt4 key购买 nike

我想知道为什么这个 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 映射。对于 floatdouble 来说,情况并非如此 - 浮点运算在这方面很棘手。更不用说 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com