【发布时间】:2020-01-05 18:23:41
【问题描述】:
假设我们要处理线性代数,使用不同类型的矩阵。而且我们有一个自定义的 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)
我错过了什么吗?有什么方法可以让泛型获得与直接调用相同的性能?
已安装框架 4.8,目标框架: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;
}
}
}
【问题讨论】:
-
您是否可以生成一个minimal reproducible example,其中包含一个包含两者的完全可执行示例?其他人更容易测试。
-
@LasseV.Karlsen 好主意,我已经编辑了这个问题,并附上了一个最小可复制示例的链接
-
不应该,因为内部可能正在使用反射。
-
存在装箱操作,当您使用泛型变体中的索引器访问矩阵元素时,IL 代码显示
box !!0/*TMatrix*/指令。这可能是性能下降的一个原因,因为装箱是非常昂贵的操作。但现在不能肯定地说,为什么在你的代码中会发生装箱 -
@FrédéricDelanchy 有一个现有的thread 将解释为什么对泛型类型参数执行装箱操作。这以及我之前的评论可能是性能差异的原因。非泛型变体中没有装箱操作
标签: c# .net generics .net-core .net-4.7.2