【问题标题】:Is there a reason why Roslyn does not optimize multiple increments?Roslyn 不优化多个增量是否有原因?
【发布时间】:2021-06-20 07:25:30
【问题描述】:

我想看看Roslyn如何优化以下sn-p:

代码

public int F(int n) {
    ++n;       
    ++n;       
    ++n;       
    ++n;       

    return n;
}              

asm

C.F(Int32)
    L0000: inc edx
    L0002: inc edx
    L0004: inc edx
    L0006: inc edx
    L0008: mov eax, edx
    L000a: ret

问题

为什么 Roslyn 不像 MSVC 这样的提前 C 编译器那样优化它? 4 x INC 较慢(4 个周期延迟与 1 甚至假设 mov 消除,并且比吞吐量所需的多 4 个微指令;https://agner.org/optimize/)。

C它的“等价物”:

int
f(void *dummy_this, int n) {
        ++n;        
        ++n;        
        ++n;        
        ++n;        

        return n;
}

asm 来自 MSVC 或带有 __attribute__((ms_abi)) 的 GCC,以使用与 C# asm 相同的 Windows x64 调用约定:https://godbolt.org/z/sK6h7KKcn

f:
        lea     eax, [rdx+4]
        ret

【问题讨论】:

标签: c# assembly x86-64 compiler-optimization roslyn


【解决方案1】:

编译器会优化。 n 虽然是一个参数,所以不能修改。 JIT 编译器必须修改参数值的副本

如果在递增之前将值分配给变量,Roslyn 编译器将消除递增。来自this Sharplab.io snippet,这段C#代码:

public int F(int i) {
    var n=i;
    ++n;       
    ++n;       
    ++n;       
    ++n;       

    return n;
} 

将被翻译成

public int F(int i)
{
    return i + 1 + 1 + 1 + 1;
}

并最终编译成这样的汇编代码:

C.F(Int32)
    L0000: lea eax, [edx+4]
    L0003: ret

【讨论】:

  • "n 虽然是一个参数,所以不能修改" - 但nInt32,一个值类型。它不是通过-ref 传递的——而且 CLR 抽象堆栈中的参数和局部变量的行为相同,所以我不明白你为什么说“它不能被修改”。
  • @Dai 是对的。 “虽然n是一个参数,所以它不能被修改”没有意义,并且也被OP的反汇编完全无效(包含参数的寄存器明显增加,只是效率不高)。将其分配给另一个变量应该没有什么区别。
  • @Peter-ReinstateMonica 我认为你是对的。这就是为什么我称这个版本为“解决方法”。不过,很高兴知道至少有某种方法可以做到这一点。
  • @Hrant:正如 cmets 关于这个问题的讨论(不幸的是现在转移到聊天),这个答案没有解释它(所以你应该不接受它)。这不是 C# 或 Windows x64 调用约定的工作方式。 (如果 int i 被 ref 获取(它不是),它的值将不会在 EDX 中传递/返回;它将是由指针。所以 inc edx 纯粹是函数的私有,并且比mov+add 或 LEA。)
  • @PeterCordes 你是绝对正确的,但我想说这个答案总比没有好。而且我也不明白为什么当有非常有价值的信息可供阅读时,它会转移到聊天。
猜你喜欢
  • 1970-01-01
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 2015-08-06
  • 1970-01-01
  • 2021-04-04
  • 1970-01-01
相关资源
最近更新 更多