gpt4 book ai didi

.net - 这个虚方法调用如何比密封方法调用更快?

转载 作者:行者123 更新时间:2023-12-05 00:39:40 24 4
gpt4 key购买 nike

我正在对虚拟成员与密封成员的表现进行一些修补。

下面是我的测试代码。

输出是

virtual total 3166ms
per call virtual 3.166ns
sealed total 3931ms
per call sealed 3.931ns

我一定是做错了什么,因为根据这个虚拟调用比密封调用快。

我在“优化代码”打开的情况下在 Release模式下运行。

编辑:在 VS 之外运行(作为控制台应用程序)时,时间接近死热。但虚拟几乎总是排在前面。
[TestFixture]
public class VirtTests
{

public class ClassWithNonEmptyMethods
{
private double x;
private double y;

public virtual void VirtualMethod()
{
x++;
}
public void SealedMethod()
{
y++;
}
}

const int iterations = 1000000000;


[Test]
public void NonEmptyMethodTest()
{

var foo = new ClassWithNonEmptyMethods();
//Pre-call
foo.VirtualMethod();
foo.SealedMethod();

var virtualWatch = new Stopwatch();
virtualWatch.Start();
for (var i = 0; i < iterations; i++)
{
foo.VirtualMethod();
}
virtualWatch.Stop();
Console.WriteLine("virtual total {0}ms", virtualWatch.ElapsedMilliseconds);
Console.WriteLine("per call virtual {0}ns", ((float)virtualWatch.ElapsedMilliseconds * 1000000) / iterations);


var sealedWatch = new Stopwatch();
sealedWatch.Start();
for (var i = 0; i < iterations; i++)
{
foo.SealedMethod();
}
sealedWatch.Stop();
Console.WriteLine("sealed total {0}ms", sealedWatch.ElapsedMilliseconds);
Console.WriteLine("per call sealed {0}ns", ((float)sealedWatch.ElapsedMilliseconds * 1000000) / iterations);

}

}

最佳答案

您正在测试内存对齐对代码效率的影响。 32 位 JIT 编译器无法为 C# 代码中大小超过 32 位、long 和 double 的值类型生成高效代码。问题的根源在于 32 位 GC 堆分配器,它只 promise 在 4 的倍数的地址上对齐分配的内存。这是一个问题,您正在增加 double 数。只有当它在一个 8 的倍数的地址上对齐时,double 才是有效的。堆栈的问题也是如此,在局部变量的情况下,它在 32 位机器上也只与 4 对齐。

L1 CPU 缓存在内部组织为称为“缓存线”的块。程序读取未对齐的 double 值时会受到惩罚。尤其是跨越缓存线末端的缓存线,必须读取来自两条缓存线的字节并将其粘合在一起。未对齐在 32 位抖动中并不少见,“x”字段恰好分配在 8 的倍数的地址上的几率仅为 50-50。如果不是,则“x”和'y' 会错位,其中之一很可能跨越缓存线。您编写测试的方式会导致 VirtualMethod 或 SealedMethod 变慢。确保让他们使用相同的字段来获得可比较的结果。

代码也是如此。交换虚拟和密封测试的代码以任意更改结果。通过这种方式,我可以毫不费力地加快密封测试的速度。鉴于速度的适度差异,您可能正在查看代码对齐问题。 x64 抖动会努力插入 NOP 以使分支目标对齐,而 x86 抖动则不会。

您还应该在循环中多次运行计时测试,至少 20 次。您可能还会观察到垃圾收集器移动类对象的效果。替身之后可能会有不同的对齐方式,从而极大地改变了时间。访问像 long 或 double 这样的 64 位值类型值有 3 个不同的时序,在 8 上对齐,在一个缓存行内在 4 上对齐,以及在两个缓存行中在 4 上对齐。以快到慢的顺序。

惩罚是陡峭的,读取跨越缓存行的 double 比读取对齐的 double 大约慢三倍。也是为什么在大对象堆中分配 double[]( double 数组)的核心原因,即使它只有 1000 个元素,远远低于 80KB 的正常阈值,LOH 的对齐保证为 8。这些对齐问题在由 x64 抖动生成的代码中完全消失,堆栈和 GC 堆都具有 8 对齐。

关于.net - 这个虚方法调用如何比密封方法调用更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4406257/

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