Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/260.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重复访问器调用的编译器优化_C#_Optimization_Compiler Construction_Accessor - Fatal编程技术网

C# 重复访问器调用的编译器优化

C# 重复访问器调用的编译器优化,c#,optimization,compiler-construction,accessor,C#,Optimization,Compiler Construction,Accessor,我最近发现,对于某些类型的金融计算,以下模式更容易遵循和测试,特别是在我们可能需要从计算的各个阶段获得数字的情况下 public class nonsensical_calculator { ... double _rate; int _term; int _days; double monthlyRate { get { return _rate / 12; }} public double days { get { return (1

我最近发现,对于某些类型的金融计算,以下模式更容易遵循和测试,特别是在我们可能需要从计算的各个阶段获得数字的情况下

public class nonsensical_calculator
{ 

   ...

    double _rate;
    int _term;
    int _days;

    double monthlyRate { get { return _rate / 12; }}

    public double days { get { return (1 - i); }}
    double ar   { get { return (1+ days) /(monthlyRate  * days)
    double bleh { get { return Math.Pow(ar - days, _term)
    public double raar { get { return bleh * ar/2 * ar / days; }}
    ....
}
显然,这通常会导致在给定公式中多次调用同一访问器。我很好奇,编译器是否足够聪明,能够在不改变状态的情况下优化这些重复调用,或者这种风格是否会带来不错的性能影响

进一步的阅读建议总是很受欢迎的

据我所知,C#编译器不会优化它,因为它不能确定副作用(例如,如果你在getter中有
accessCount++
),请看下面的一个例子

根据这一回答:

C#编译器从来没有做过这种优化;如前所述,这样做需要编译器窥视被调用的代码,并验证其计算的结果在被调用代码的生命周期内没有改变。C#编译器不这样做

JIT编译器可能会。没有理由不能。所有的代码都在那里。内联属性getter是完全自由的,如果jitter确定内联属性getter返回一个可以缓存在寄存器中并重新使用的值,那么它可以自由地这样做。(如果您不希望它这样做,因为该值可以在另一个线程上修改,那么您已经有一个争用条件错误;请在担心性能之前修复该错误。)


只是一个注释,作为C编译器编译器上的埃里克,我相信他的答案是:

< P>,在这一点上稍有不同,考虑到代码编译到IL之后,属性实际上只是方法周围的包装。因此,如果不是这样:

public class nonsensical_calculator
{
    double bleh
    {
        get { return Math.Pow(ar - days, _term); }
    }
    // etc.
}
你有这个:

public class nonsensical_calculator
{
    double GetBleh()
    {
        return Math.Pow(ar - days, _term);
    }
}
您希望编译器为您优化方法调用吗

我不是抖动方面的专家,但我怀疑即使抖动也会“缓存”这一点;它必须跟踪各种状态,并在任何相关字段发生更改时使条目无效,尽管.NET抖动非常可怕,但我认为它没有那么聪明。它可以内联方法,但这通常不会在性能方面产生巨大的差异

总之,不要依赖编译器或抖动来为您进行这些优化。此外,您可能会考虑遵循不在属性吸收器中投入昂贵计算的通用设计准则,因为它对调用方来说是便宜的,即使它可能不是。 如果需要性能,则在相关字段更改时预计算这些值。或者,更好的办法是,使用(免费)或之类的工具来分析代码,看看性能成本到底在哪里。在没有评测的情况下进行优化就像蒙着眼睛射击。

没错,C#编译器不会进行这样的优化。但JIT编译器确实如此。您发布的所有getter都足够小,可以内联,从而直接访问该字段

例如:

static void Main(string[] args) {
  var calc = new nonsensical_calculator(42);
  double rate = calc.monthlyRate;
  Console.WriteLine(rate);
}
生成:

00000000  push        ebp                          ; setup stack frame
00000001  mov         ebp,esp 
00000003  sub         esp,8 
00000006  mov         ecx,349DFCh                  ; eax = new nonsensical_calculator
0000000b  call        FFC50AD4 
00000010  fld         dword ptr ds:[006E1590h]     ; st0 = 42
00000016  fstp        qword ptr [eax+4]            ; _rate = st0
00000019  fld         qword ptr [eax+4]            ; st0 = _rate
0000001c  fdiv        dword ptr ds:[006E1598h]     ; st0 = st0 / 12
00000022  fstp        qword ptr [ebp-8]            ; rate = st0
      Console.WriteLine(rate);
// etc..
注意构造函数调用和属性getter是如何消失的,它们被内联到Main()中。代码直接访问_rate字段。即使calc变量消失,引用也保存在eax寄存器中


地址19处的指令表明可以在优化器上做更多的工作。时间允许。

一些随意的想法

首先,正如其他人所指出的,C#编译器不进行这种优化,尽管抖动可以自由进行

第二,回答性能问题的最佳方法是尝试并观察。秒表班是你的朋友。两种方法都试10亿次,看看哪一种更快;那你就知道了

第三,当然,花时间优化已经足够快的东西是没有意义的。在您花费大量时间进行基准测试之前,请花一些时间分析和寻找热点。这不太可能

第四,另一个答案建议将中间结果存储在局部变量中。请注意,在某些情况下,这样做会使事情变得相当快,而在另一些情况下,这样做会使事情变得更慢。有时,不必要地重新计算结果比存储结果并在需要时再次查找结果要快

这怎么可能?带有少量寄存器的芯片架构——我在看x86——要求抖动非常明智地判断寄存器中哪些是局部变量,哪些是堆栈访问。鼓励抖动在一个寄存器中放入不经常使用的内容,有时意味着将其他内容从该寄存器中挤出,这将比您不经常使用的值从寄存器中获得更多好处


简言之:不要试图猜测舒适扶手椅的抖动;现实世界代码的行为可能与直觉大相径庭。根据现实的经验测量做出绩效决策

通常,编译器没有足够的信息来优化对同一属性/方法的多个调用。如果您真的需要优化,您可以通过将计算结果保存在(其他)成员变量中来完成。每当值计算起来很昂贵并且可能被多次调用时,我就会这样做;计算只做了一次,额外的开销非常小。非常感谢-非常好的资源。非常感谢-这正是我需要知道的。我不想过分沉迷于微观优化,但同时我讨厌那些毫无必要的浪费代码。在我当前的项目完成后,我将不得不花一点时间来处理这个问题。这正是我热爱编程的原因——我偶尔会在脑海中想到,我知道自己在做什么,但还不正确:D