使用具体类型而不是界面是否更好的性能?

use*_*095 15 .net c# performance

我遇到了一些规则(建议)使用混凝土ListDictionary,而不是IListIDictionary给予,显示通过该接口访问样本测试是相当慢一点.例如,将10000个值添加到列表然后Count在列表上执行10亿次表示通过接口执行此操作比通过具体类执行它慢28倍.即,通过具体类需要80ms,通过界面需要2800ms,这表明它通过界面的速度非常慢.鉴于此,使用具体类是合理的.有没有理由说接口这么慢?(可能更多的是针对那些了解更多关于.net内部的人).

Nik*_*iki 8

我认为如果你看一下反汇编就很明显了:

IList版本编译为:

            for (int i = 0; i < 1000000000; i++) 
0000003d  xor         edi,edi 
            { 
                count = lst.Count; 
0000003f  mov         ecx,esi 
00000041  call        dword ptr ds:[00280024h] 
00000047  mov         ebx,eax 
            for (int i = 0; i < 1000000000; i++) 
00000049  inc         edi 
0000004a  cmp         edi,3B9ACA00h 
00000050  jl          0000003F 
            }
Run Code Online (Sandbox Code Playgroud)

访问权限IList.Count被编译成call指令.

List另一方面,该版本内联:

            for (int i = 0; i < 1000000000; i++) 
0000003a  xor         edx,edx 
0000003c  mov         eax,dword ptr [esi+0Ch] 
0000003f  mov         esi,eax 
00000041  inc         edx 
00000042  cmp         edx,3B9ACA00h 
00000048  jl          0000003F 
            }
Run Code Online (Sandbox Code Playgroud)

call这里没有说明.只是循环中的mov,inc,cmp和jl指令.当然这更快.

但请记住:通常情况下,您正在对列表中的内容进行某些操作,而不仅仅是迭代它.这通常比单个函数调用花费更长的时间,因此调用接口方法很少会导致任何性能问题.


Hen*_*man 6

使用接口的主要原因是灵活性,关注点分离等.

所以我仍然建议在大多数情况下使用接口(不是全部,只是在适当的地方),并且只考虑在存在实际性能问题时切换到具体类.

并且,在没有GC.Collect()和在发布模式下运行您的基准测试后,我得到96和560毫秒.差异很小.


Ste*_*ven 6

你的表现测试显然是错误的.你正在测试很多不同的东西.首先GC.Collect会触发终结器线程,它会影响其后运行的所有内容的性能.接下来,您不仅要测试调用接口方法所花费的时间,还要花费大量时间将数据复制到新数组(因为您正在创建大型数组)并收集它们! - 因此接口调用之间存在差异实例调用将完全迷失.

这是一个测试,我测试接口调用的原始性能开销.在Visual Studio外部以发布模式运行时:

public interface IMyInterface
{
    void InterfaceMethod();
}

public class MyClass : IMyInterface
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void InterfaceMethod()
    {
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void InstanceMethod()
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        // JITting everyting:
        MyClass c = new MyClass();
        c.InstanceMethod();
        c.InterfaceMethod();
        TestInterface(c, 1);
        TestConcrete(c, 1);
        Stopwatch watch = Stopwatch.StartNew();
        watch.Start();
        var x = watch.ElapsedMilliseconds;

        // Starting tests:
        watch = Stopwatch.StartNew();

        TestInterface(c, Int32.MaxValue - 2);

        var ms = watch.ElapsedMilliseconds;

        Console.WriteLine("Interface: " + ms);

        watch = Stopwatch.StartNew();

        TestConcrete(c, Int32.MaxValue - 2);

        ms = watch.ElapsedMilliseconds;

        Console.WriteLine("Concrete: " + ms);
    }

    static void TestInterface(IMyInterface iface, int iterations)
    {
        for (int i = 0; i < iterations; i++)
        {
            iface.InterfaceMethod();
        }
    }

    static void TestConcrete(MyClass c, int iterations)
    {
        for (int i = 0; i < iterations; i++)
        {
            c.InstanceMethod();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Interface: 4861
Concrete: 4236
Run Code Online (Sandbox Code Playgroud)