我一直在寻找mscorlib,看看泛型集合如何优化他们的枚举器,我偶然发现了这个:
// in List<T>.Enumerator<T>
public bool MoveNext()
{
List<T> list = this.list;
if ((this.version == list._version) && (this.index < list._size))
{
this.current = list._items[this.index];
this.index++;
return true;
}
return this.MoveNextRare();
}
堆栈大小为3,字节码的大小应为80字节。 MoveNextRare
方法的命名让我脚踏实地,它包含一个错误案例以及一个空的收集案例,所以很明显这违反了关注点。
我假设MoveNext
方法以这种方式拆分以优化堆栈空间并帮助JIT,我想对我的一些性能瓶颈做同样的事情,但是没有硬数据,我不这样做希望我的伏都教编程变成货物崇拜;)
谢谢! 弗洛里安
答案 0 :(得分:3)
如果你想考虑List<T>.Enumerator
为了表现而“奇怪”的方式,首先考虑一下:它是一个可变的结构。随意恐惧地退缩;我知道我这样做。
最终,如果没有基准测试/分析他们在您的特定应用程序中产生的差异,我不会开始模仿BCL的优化。它可能适合BCL但不适合你;不要忘记BCL在安装时会经历整个NGEN类似的服务。找出适用于 应用程序的唯一方法是测量它。
你说你想为你的性能瓶颈尝试同样的事情:这表明你已经知道瓶颈,这表明你已经有了某种衡量标准。因此,尝试这种优化并测量它,然后看看性能的增益是否值得随之而来的可读性/维护的痛苦。
尝试某些事情并对其进行衡量,然后根据这些证据作出决定,没有任何货物狂热。
答案 1 :(得分:1)
将它分成两个函数有一些优点:
如果要内联该方法,则只会内联快速路径,并且错误处理仍然是函数调用。这可以防止内联花费太多额外空间。但是80字节的IL可能仍然高于内联的阈值(它曾被记录为32字节,不知道自.NET 2.0以来它是否发生了变化)。
即使没有内联,该函数也会更小并且更容易适应CPU的指令缓存,并且由于慢速路径是分开的,所以每次快速路径都不需要将其提取到缓存中
它可能有助于CPU分支预测器针对更常见的路径进行优化(返回true)。
我认为MoveNextRare总是会返回false,但是通过像这样构造它会变成一个尾调用,如果它是私有的并且只能从这里调用那么JIT理论上可以在这两个之间建立一个自定义调用约定这些方法只包含一条没有序言的jmp指令,也没有重复的结尾。