gpt4 book ai didi

c# - 为什么数组项分配会降低 C# 程序的性能?

转载 作者:IT王子 更新时间:2023-10-29 04:43:23 24 4
gpt4 key购买 nike

总结

在处理大型文本文件时,我遇到了以下我无法解释的(意外的)性能下降。我对这个问题的目标是:

  • 了解导致下述减速的原因
  • 了解如何快速初始化大型非原始数组

问题

  • 数组包含非原始引用项
    • 不是int[]而是MyComplexType[]
    • MyComplexType 是类,不是结构
    • MyComplexType 包含一些 string 属性
  • 数组是预先分配的
  • 数组很大
  • 如果创建并使用一个项目而不分配给数组,程序速度很快
  • 如果创建一个项目然后分配给数组项目,程序会显着变慢
    • 我希望数组项分配是一个简单的引用分配,但根据我下面测试程序的结果,情况似乎并非如此

测试程序

考虑以下 C# 程序:

namespace Test
{
public static class Program
{
// Simple data structure
private sealed class Item
{
public Item(int i)
{
this.Name = "Hello " + i;
//this.Name = "Hello";
//this.Name = null;
}
public readonly string Name;
}

// Test program
public static void Main()
{
const int length = 1000000;
var items = new Item[length];

// Create one million items but don't assign to array
var w = System.Diagnostics.Stopwatch.StartNew();
for (var i = 0; i < length; i++)
{
var item = new Item(i);
if (!string.IsNullOrEmpty(item.Name)) // reference the item and its Name property
{
items[i] = null; // do not remember the item
}
}
System.Console.Error.WriteLine("Without assignment: " + w.Elapsed);

// Create one million items and assign to array
w.Restart();
for (var i = 0; i < length; i++)
{
var item = new Item(i);
if (!string.IsNullOrEmpty(item.Name)) // reference the item and its Name property
{
items[i] = item; // remember the item
}
}
System.Console.Error.WriteLine(" With assignment: " + w.Elapsed);
}
}
}

它包含两个几乎相同的循环。每个循环创建一百万个 Item 类实例。第一个循环使用创建的项目,然后丢弃引用(不将其保存在 items 数组中)。第二个循环使用创建的项目,然后将引用存储在 items 数组中。数组项分配是循环之间的唯一区别。

我的结果

  • 当我在我的机器上运行 Release 构建(打开优化)时,我得到以下结果:

    Without assignment: 00:00:00.2193348
    With assignment: 00:00:00.8819170

    有数组赋值的循环比没有赋值的循环慢得多(慢 4 倍左右)。

  • 如果我更改 Item 构造函数以将常量字符串分配给 Name 属性:

    public Item(int i)
    {
    //this.Name = "Hello " + i;
    this.Name = "Hello";
    //this.Name = null;
    }

    我得到以下结果:

    Without assignment: 00:00:00.0228067
    With assignment: 00:00:00.0718317

    有赋值的循环仍然比没有赋值的循环慢 3 倍

  • 最后,如果我将 null 分配给 Name 属性:

    public Item(int i)
    {
    //this.Name = "Hello " + i;
    //this.Name = "Hello";
    this.Name = null;
    }

    我得到以下结果:

    Without assignment: 00:00:00.0146696
    With assignment: 00:00:00.0105369

    一旦没有分配字符串,没有分配的版本最终会稍微慢一些(我假设是因为所有这些实例都被释放用于垃圾收集)

问题

  • 为什么数组项赋值会大大降低测试程序的速度?

  • 是否有可以加快分配速度的属性/语言构造/等?

PS:我尝试使用 dotTrace 调查减速,但没有定论。我看到的一件事是在有赋值的循环中比在没有赋值的循环中有更多的字符串复制和垃圾收集开销(即使我预期相反)。

最佳答案

我怀疑大部分时间问题都与内存分配有关。

当您将项目分配到数组中时,它们永远不会符合垃圾回收条件。当您将字符串作为不是常量(内部)或 null 的属性时,这将导致您的内存分配要求上升。

在第一种情况下,我怀疑正在发生的事情是您正在快速搅动对象,因此它们留在 Gen0 中,可以快速进行 GC,并且内存段可以重用。这意味着您永远不必从操作系统分配更多内存。

在第二种情况下,您在对象中创建字符串,这两个对象都是两次分配,然后存储它们以使它们不符合 GC 的条件。在某些时候,您需要获得更多内存,因此您将获得分配的内存。

至于最后的检查 - 当您将 Name 设置为 null 时,if (!string.IsNullOrEmpty(item.Name)) 检查将阻止它被添加。因此,这两个代码路径以及时间变得(有效地)相同,尽管第一个路径稍微慢一些(很可能是由于 JIT 第一次运行)。

关于c# - 为什么数组项分配会降低 C# 程序的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19797916/

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