我有一个接口I
,一个抽象类A
,这两个具体的类,以及一个用作基线的具体类C
。
interface I
{
void Do();
}
abstract class A
{
public abstract void Do();
}
class C
{
public void Do() {}
}
class IImpl : I
{
public void Do() {}
}
class AImpl : A
{
public override void Do() {}
}
我使用C.Do()
对I.Do()
,A.Do()
和BenchmarkDotNet
的来电进行了基准测试。
public class Bench
{
private C _c;
private I _i;
private A _a;
[Setup]
public void Setup()
{
_c = new C();
_i = new IImpl();
_a = new AImpl();
}
[Benchmark(Baseline = true)]
public void ConcreteCall()
{
_c.Do();
}
[Benchmark]
public void InterfaceCall()
{
_i.Do();
}
[Benchmark]
public void AbstractCall()
{
_a.Do();
}
}
基准测试结果始终显示调用A.Do()
的速度超过I.Do()
。
Method | Mean | StdErr | StdDev | Scaled | Scaled-StdDev |
-------------- |---------- |---------- |---------- |------- |-------------- |
ConcreteCall | 0.0673 ns | 0.0114 ns | 0.0440 ns | 1.00 | 0.00 |
InterfaceCall | 1.4944 ns | 0.0084 ns | 0.0325 ns | 62.92 | 74.68 |
AbstractCall | 0.8178 ns | 0.0139 ns | 0.0539 ns | 34.43 | 40.99 |
我在此结构上尝试了一些变体,例如从Impl
和A
派生了一个I
类,或者通过A
实现线性化层次结构I
,每次都重现同样的效果。
当然,差异并不大 - 朋友之间的距离是700皮秒? - 但我想了解.NET运行时中发生了什么。我理解为什么C.Do()
是迄今为止最快的 - 在查找方法实现方面没有间接性 - 但为什么通过抽象类的调用通过接口击败了调用?我在OSX上使用.NET Core 1.1和x64 RyuJit。