我知道静态方法可以通过.Net(和Mono)中的JIT优化来内联
我的问题是,是否可以实现访问其自身状态的实例方法?
例如:
public class CaseSensitiveLiteralStringMatcher : IStringMatcher
{
private readonly LiteralToken _token;
public CaseSensitiveLiteralStringMatcher(LiteralToken token)
{
_token = token;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsMatch(char containsChar, int position)
{
return containsChar == _token.Value[position];
}
}
上面的方法调用是否内联,即使它不是静态的并访问一些私有成员?
我的结论是,实例方法可以内联,但虚方法不能,因为调用的实际方法可以在运行时更改,并且无法使用源代码的静态分析来建立。
出于这个原因,我在我的问题中显示的方法可以内联,如果它不是接口方法 - 因为这意味着它必须在运行时通过vtable查找调度它是虚拟的。
说,有JIT优化技术可以针对“常见”情况优化虚拟方法内联,但是当内联方法与运行时所需的方法调用不匹配时,这些技术会带来回退,这意味着某些代码路径可能会受益更多来自内联其他人。
好的。我有结果。答案似乎是JIT可以内联一个实现接口并访问或修改类成员的方法。
我的结果是:
即具有和不具有界面的相同性能。此外,如果没有编译器积极的内联指令,性能仍然相同。
测试代码:
class Program
{
internal interface IFastProcessor
{
void Process(int i);
}
internal sealed class FastProcessorImpl : IFastProcessor
{
private int number;
public FastProcessorImpl(int number)
{
this.number = number;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Process(int i)
{
number = ((number + i) / (number + i)) * number;
}
}
internal sealed class FastProcessor
{
private int number;
public FastProcessor(int number)
{
this.number = number;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Process(int i)
{
number = ((number + i) / (number + i)) * number;
}
}
static void Main(string[] args)
{
var sw1 = new Stopwatch();
var processor1 = new FastProcessor(10);
sw1.Start();
for (int i = 1; i < 10000000; i++)
{
processor1.Process(i);
}
sw1.Stop();
var sw2 = new Stopwatch();
var processor2 = (IFastProcessor)new FastProcessorImpl(10);
sw2.Start();
for (int i = 1; i < 10000000; i++)
{
processor2.Process(i);
}
sw2.Stop();
var number = 10;
var sw3 = new Stopwatch();
sw3.Start();
for (int i = 1; i < 10000000; i++)
{
number = ((number + i) / (number + i)) * number;
}
sw3.Stop();
Console.WriteLine($"Class: {sw1.ElapsedMilliseconds}ms, Interface: {sw2.ElapsedMilliseconds}ms, Inline: {sw3.ElapsedMilliseconds}ms");
}
}
更新:我还尝试了一个带有虚方法的基类。令我极为惊讶的是,这也与内联版本完全相同,这意味着编译器可能正在优化虚拟调用,无论如何都允许JIT内联。所以我无法确定接口与虚拟方法的问题。但是,另一方面,可以肯定地说,在OPs问题中,我没有看到为什么不会内联该方法的原因。