通用方法与非通用方法之间的性能差异

huangapple go评论89阅读模式
英文:

Performance difference between generic and non generic method

问题

让我们以示例为例,假设我们想要处理线性代数,使用不同类型的矩阵。并且我们有一个自定义的Matrix类,它实现了:

interface IMatrix
{
    double this[int i, int j] { get; set; }
    int Size { get; }
}

我想要实现矩阵乘法。我曾以为以下两种方法:

static void Multiply<TMatrix>(TMatrix a, TMatrix b, TMatrix result) where TMatrix : IMatrix

static void Multiply(Matrix a, Matrix b, Matrix result)

(当然,实现方式类似)会在内部产生完全相同的IL代码,从而具有相同的性能。但事实并非如此:第一种方法比第二种方法慢四倍。从IL代码来看,泛型方法似乎类似于通过接口调用:

static void Multiply(IMatrix a, IMatrix b, IMatrix result)

我是否遗漏了什么?是否有办法通过泛型获得与直接调用相同的性能?


已安装的Framework版本为4.8,目标Framework版本为4.7.2(也测试过.NET Core 3)

方法实现如下:

static void Multiply(Matrix a, Matrix b, Matrix result)
{
    for (int i = 0; i < a.Size; i++)
    {
        for (int j = 0; j < a.Size; j++)
        {
            double temp = 0;
            for (int k = 0; k < a.Size; k++)
            {
                temp += a[i, k] * b[k, j];
            }
            result[i, j] = temp;
        }
    }
}

最小可重现示例

英文:

Let's say for the sake of the example that we want to work on linear algebra, with different type of matrices. And that we have a custom Matrix class that implements :

interface IMatrix
{
    double this[int i, int j] { get; set; }
    int Size { get; }
}

I want to implement Matrix multiplication. I was under the impression that both methods :

static void Multiply&lt;TMatrix&gt;(TMatrix a, TMatrix b, TMatrix result) where TMatrix : IMatrix

and

static void Multiply(Matrix a, Matrix b, Matrix result)

(with similar implementation of course) Would internally produce the exact same IL and hence the same performances. It is not the case : the first one is four times slower than the second one. Looking at the IL, it seems the generic one is similar to a call through interface :

static void Multiply(IMatrix a, IMatrix b, IMatrix result)

Am I missing something ? Is there any way to get the same performances with generics than with a direct call ?


Installed Framework 4.8, Target Framework : 4.7.2 (also tested with .Net Core 3)

Method implementation :

static void Multiply(Matrix a, Matrix b, Matrix result)
{
    for (int i = 0; i &lt; a.Size; i++)
    {
        for (int j = 0; j &lt; a.Size; j++)
        {
            double temp = 0;
            for (int k = 0; k &lt; a.Size; k++)
            {
                temp += a[i, k] * b[k, j];
            }
            result[i, j] = temp;
        }
    }
}

Minimal reproductible example

答案1

得分: 4

.NET只会为所有引用类型生成一次通用方法的代码。而且该代码必须通过IMatrix接口调用,因为不同的实现类型可能使用不同的方法实现该接口。所以这只是一个接口调用。

然而,如果你将Matrix定义为struct而不是class,JIT编译器将生成通用方法的类型特定实现,在这种情况下,接口调用可以被优化掉。

英文:

.NET will only generate code for a generic method once for all reference types. And that code must call through the IMatrix interface, as the various implementing types might implement the interface with different methods. So it simply is an interface call.

However if you make Matrix a struct instead of a class the JITter will generate a type-specific implementation of the generic methods, and in that the interface call can be optimized away.

huangapple
  • 本文由 发表于 2020年1月6日 02:23:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/59602921.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定