gpt4 book ai didi

c# - 为什么在 .NET 5 中用作泛型类型参数时引用类型为 "slower"?

转载 作者:行者123 更新时间:2023-12-05 00:53:47 25 4
gpt4 key购买 nike

今天我遇到了这个问题:当使用引用类型作为外部泛型类型的类型参数时,嵌套类型中的其他方法会慢约 10 倍。我使用哪种类型并不重要——所有引用类型似乎都会“减慢”代码。 (抱歉标题,也许有人能找到更合适的。)

使用 .NET 5/Release 构建测试。

我错过了什么?

编辑 2:

我将尝试进一步解释问题并清理代码。如果你还想看旧版本,这里有一个副本:

https://gist.github.com/sneusse/1b5ee408dd3fdd74fcf9d369e144b35f

新代码说明了同样的问题,但希望能减少分心。

  • 类(class)WthGeneric<T>被实例化了两次
  • 第一个实例使用任何引用类型作为类型参数(这里:object)
  • 第二个实例使用任何值类型作为类型参数(这里:long)
  • 因为两者都是同一个类的实例,所以都具有相同的方法WhatIsHappeningHere
  • 这两个实例都没有以任何方式使用泛型参数。

这就引出了一个问题:为什么同一个实例方法的运行时间比另一个高10倍?

输出:

System.Object: 516,8448ms
System.Int64: 50,6958ms

代码:

using System;
using System.Diagnostics;
using System.Linq;

namespace Perf
{
public interface IWthGeneric
{
int WhatIsHappeningHere();
}

// This is a generic class. Note that the generic
// type argument 'T' is _NOT_ used at all!
public class WthGeneric<T> : IWthGeneric
{
// This is part of the issue.
// If this field is not accessed or moved *outside*
// of the generic 'WthGeneric' class, the code is fast again
// ** also with reference types **
public static int StaticVar = 12;

static class NestedClass
{
public static int Add(int value) => StaticVar + value;
}

public int WhatIsHappeningHere()
{
var x = 0;
for (int i = 0; i < 100000000; i++)
{
x += NestedClass.Add(i);
}
return x;
}
}

public class RunMe
{
public static void Run()
{
// The interface is used so nothing could ever get inlined.
var wthObject = (IWthGeneric) new WthGeneric<object>();
var wthValueType = (IWthGeneric) new WthGeneric<long>();

void Test(IWthGeneric instance)
{
var sw = Stopwatch.StartNew();
var x = instance.WhatIsHappeningHere();
Console.WriteLine(
$"{instance.GetType().GetGenericArguments().First()}: " +
$"{sw.Elapsed.TotalMilliseconds}ms");
}

for (int i = 0; i < 10; i++)
{
Test(wthObject);
Test(wthValueType);
}
}
}
}

最佳答案

不是 100% 肯定,但我想我知道为什么 JIT 没有对此进行优化:

据我了解,每个泛型类型通常只有一个版本的引用类型的 JITted 代码,名为 System.__Canon 并传入类型参数作为实际的 typeref 参数。而对于值类型,每一个都是单独生成的。

这是因为引用类型在 JIT 看来总是相同的:指向对象的指针,该对象的第一个字段作为指向其 typeref 和方法表的指针。但是 valuetypes 都是不同的,所以每个都必须定制。


您说您不使用类型参数,但实际上您使用了。 当您访问泛型类型的静态字段时,每个实例化的泛型类型都需要静态字段的单独副本。

因此代码现在必须对类型参数的 typeref 进行指针查找以获取静态字段的值。

但在 valuetype 版本中,typeref 是静态已知的,因此每次都是直接的内存访问。

关于c# - 为什么在 .NET 5 中用作泛型类型参数时引用类型为 "slower"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67061844/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com