gpt4 book ai didi

c# - 为什么局部变量引用会导致性能大幅下降?

转载 作者:IT王子 更新时间:2023-10-29 03:49:28 24 4
gpt4 key购买 nike

考虑以下简单程序:

using System;
using System.Diagnostics;

class Program
{
private static void Main(string[] args)
{
const int size = 10000000;
var array = new string[size];

var str = new string('a', 100);
var sw = Stopwatch.StartNew();
for (int i = 0; i < size; i++)
{
var str2 = new string('a', 100);
//array[i] = str2; // This is slow
array[i] = str; // This is fast
}
sw.Stop();
Console.WriteLine("Took " + sw.ElapsedMilliseconds + "ms.");
}
}

如果我运行它,它会相对较快。如果我取消注释“慢”行并注释掉“快”行,它会慢 5 倍以上。请注意,在这两种情况下,它都会在循环内初始化字符串“str2”。这两种情况都没有优化(这可以通过查看 IL 或反汇编来验证)。

在这两种情况下,代码似乎都在做同样多的工作。它需要分配/初始化一个字符串,然后分配一个对数组位置的引用。唯一的区别是该引用是本地变量“str”还是“str2”。

为什么将引用分配给“str”与“str2”会产生如此大的性能差异?

如果我们看一下反汇编,就会有区别:

(fast)
var str2 = new string('a', 100);
0000008e mov r8d,64h
00000094 mov dx,61h
00000098 xor ecx,ecx
0000009a call 000000005E393928
0000009f mov qword ptr [rsp+58h],rax
000000a4 nop

(slow)
var str2 = new string('a', 100);
00000085 mov r8d,64h
0000008b mov dx,61h
0000008f xor ecx,ecx
00000091 call 000000005E383838
00000096 mov qword ptr [rsp+58h],rax
0000009b mov rax,qword ptr [rsp+58h]
000000a0 mov qword ptr [rsp+38h],rax

“慢速”版本有两个额外的“mov”操作,而“快速”版本只有一个“nop”。

谁能解释一下这里发生了什么?很难看出两个额外的 mov 操作如何导致 >5 倍的减速,特别是因为我预计大部分时间应该花在字符串初始化上。感谢您提供任何见解。

最佳答案

你是对的,代码在两种情况下所做的工作量大致相同。

但垃圾收集器在这两种情况下最终会做截然不同的事情。

str 版本中,在给定时间最多有两个字符串实例处于事件状态。这意味着(几乎)第 0 代中的所有新对象都会死亡,不需要将任何对象提升到第 1 代。由于第 1 代根本没有增长,GC 没有理由尝试昂贵的“完整收集”。

str2 版本中,所有新的字符串实例都是存活的。对象被提升到更高的世代(这可能涉及将它们移动到内存中)。此外,由于高代正在增长,GC 偶尔会尝试运行完整的收集。

请注意,.NET GC 往往花费的时间与事件对象的数量成线性关系:事件对象需要遍历并移开,而死对象根本不需要任何费用(它们只是被覆盖下次分配内存)。

这意味着 str 是垃圾收集器性能的最佳情况;而 str2 是最坏的情况。

看看 GC performance counters对于您的程序,我怀疑您会在程序之间看到非常不同的结果。

关于c# - 为什么局部变量引用会导致性能大幅下降?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37120893/

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