Xen*_*ate 4 c# optimization roslyn
请考虑以下代码:
public static void M() {
A(V);
A(V);
A(V);
}
public static void V() {
}
public static void A(Action x) {
x();
}
Run Code Online (Sandbox Code Playgroud)
这可以在幕后编译为:
public static void M() {
A(new Action(V));
A(new Action(V));
A(new Action(V));
}
Run Code Online (Sandbox Code Playgroud)
但是,我们可以编写自己的简单性能改进,以减少不必要的垃圾:
private static readonly Action v = new Action(V);
A(v);
A(v);
A(v);
Run Code Online (Sandbox Code Playgroud)
对于这个非常简单的案例,Roslyn有什么理由无法做出类似的优化吗?
如果答案是否定的,那么当方法不是静态但实例成员时呢?什么时候捕获了封闭变量?
Eri*_*ert 17
我们可以编写自己的简单性能改进,减少不必要的垃圾
您重新发现了一个常见的子表达式消除的特殊情况- 优化识别两个或多个表达式何时具有完全相同的值,计算一次值,并将其存储在要重用的变量中.
在继续之前,我提醒你,所有所谓的"优化"实际上都是为了另一件事.您建议的优化会在每次调用时产生少量的收集压力,而不是内存泄漏.静态字段中的缓存值将成为gen 2堆的永久成员.那值得吗?这是一个你想通过实际测量来回答的问题.
对于这个非常简单的案例,Roslyn有什么理由无法做出类似的优化吗?
原则上没有理由说明如果优化不会对程序的行为产生不可接受的变化,则无法执行此优化.
特别是,优化导致两个先前值相等但不等于引用的委托等于引用相等.这很可能是可以接受的.
实际上,实现优化需要花费大量精力来设计,实现,测试和维护执行优化的代码.C#没有实现常见的子表达式消除优化.这种优化具有很差的优势.很少有人编写可以从优化中受益的代码,并且优化很小,如您所知,如果您愿意,很容易"手动"进行优化.
我注意到C#在lambdas上做了类似的缓存.它不会执行常见的子表达式消除,但它只会生成一定的lambdas并缓存结果:
void M() { Action x = () => {}; ... }
Run Code Online (Sandbox Code Playgroud)
就像你写的一样生成:
static Action anon = null;
void M()
{
if (anon == null) anon = () => {};
Action x = anon;
...
Run Code Online (Sandbox Code Playgroud)
如果答案是否定的,那么当方法不是静态但实例成员时呢?
原则上没有理由说明如果优化不会对程序的行为产生不可接受的变化,则无法执行此优化.
我注意到在这种情况下,需要进行优化以推断出实例当然是相同的.如果不这样做,将无法保持程序行为不得改变的不变量.
同样,在实践中,C#不会消除常见的子表达式.
什么时候捕获了封闭变量?
抓住了什么?你刚才谈到方法组转换给代表,显然现在我们正在谈论转换为代表的lambdas.
C#规范明确指出编译器可以选择在相同的lambda上进行公共子表达式消除,或者不选择,因为它认为合适.
原则上没有理由说明如果优化不会对程序的行为产生不可接受的变化,则无法执行此优化.由于规范明确指出允许此优化,因此根据定义可接受.
同样,在实践中,C#不会消除常见的子表达式.
也许你在这里注意到一种趋势.问题的答案"是这样的,允许这样的优化吗?" 几乎总是"是的,如果它不会对程序的行为产生不可接受的变化".但问题的答案是"C#在实践中实现了这样的优化吗?" 通常没有.
如果您想了解编译器执行的优化的一些背景知识,我在2009年对它们进行了描述.
Roslyn在大多数情况下更好地完成了这些优化.例如,Roslyn在将临时值和局部变形为短暂而非持久变量方面做得更好.我完全重写了可空算术优化器; 我的八篇系列文章描述了这里的情况.而且还有很多改进.我们从未考虑过做CSE.