C# 长度为 1 的数组与单值、性能和内存开销
Posted
技术标签:
【中文标题】C# 长度为 1 的数组与单值、性能和内存开销【英文标题】:C# length 1 array versus single value, performance and memory overhead 【发布时间】:2020-08-17 11:39:35 【问题描述】:问题:
使用长度为 1 的数组而不是直接使用值的性能和内存开销是多少?
private Item[] item = new Item[1];
vs.
private Item item;
用法:
我有一个抽象基类 ItemHolder,它由 SingleItemHolder 和 MultipleItemHolder 类继承。第一个持有单个项目作为主要值,而另一个持有一个列表。要访问该值,我看到了三种可能性:
向基类添加两个方法
public abstract Item GetItem();
public abstract Item[] GetItems(int amount);
SingleItemHolder 的缺点是没有必要的方法来获取多个项目,而每个定义只有一个。 另一种方法是仅实现第二种方法并将单个值作为长度为 1 的数组传递
public override Item[] GetItems()
return new[] storedItem ;
或首先将单个值存储为长度为 1 的数组
private Item[] item = new Item[1];
public override Item[] GetItems()
return item;
通常,多件物品和单件物品的使用频率相同。所讨论的方法很可能在整个游戏世界中每个游戏帧被调用数十次。因此,我想知道哪个版本最有效,或者更一般地说,长度为 1 的数组与单个值相比有什么开销差异。
【问题讨论】:
值得注意的是,数组本身是 C# 中的对象,因此这可能会对开销产生微小的影响,也可能不会产生影响:docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/… 另外,如果Item
是一个结构,那么向它添加另一个指针会增加少量开销。如果您希望避免返回值类型(例如结构),请查看 ref
返回:docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
遗憾的是,Item 现在是一个类。无论如何,我不知道 ref 有一些非常有趣的用途,谢谢。
另外,导致它成为对象本身的数组开销是我忽略的一个有趣的观察,也谢谢你
【参考方案1】:
只是一个简短的说明。在 .NET 世界中,最大的性能问题是 GC 可能很容易将应用程序锁定 50-100 毫秒。在从对象或单值数组中读取数据时,您可能不会看到有很大的不同。但是如果你需要大量创建这样的对象,你可能会受到惩罚。您当然应该避免从 getters 代码创建对象。
我认为这篇文章可能会有所帮助:https://michaelscodingspot.com/avoid-gc-pressure/。还可以考虑使用一些 Profile 工具来检查它实际需要多少时间。我更喜欢使用 MS 提供的 PerfView。开始使用它可能需要一些时间,但您肯定会从结果中受益。
【讨论】:
有意思,谢谢。你有一个链接可以让我更详细地了解这种锁定吗? 我认为这篇文章可能会有所帮助:michaelscodingspot.com/avoid-gc-pressure。还可以考虑使用一些 Profile 工具来检查它实际需要多少时间。我更喜欢使用 MS 提供的 PerfView。开始使用它可能需要一些时间,但您肯定会从结果中受益。【参考方案2】:使用长度为 1 的数组而不是直接使用值的性能和内存开销是多少?
这完全取决于编译器优化、JiT 编译器和索引访问器修剪现在对该数组的感觉。在它的核心,一切都是指针。该进程不关心它是否指向一个函数、单个 int 或 int 数组的开头。除了跳转到 Indexer 函数和 Indexer 健全性检查之外,应该没有性能影响。甚至可以修剪或至少内联一个索引器访问。
理论上可能优化看到你的new int[1]
并决定一个基本的int
会在那里做。如果仅在运行时定义大小,JiT 甚至可以做到这一点。但是,这不太可能。
如果你需要那里有一个数组,你需要一个那里的数组。有时它为 1 的奇怪情况无需担心。即使大小为 1 的数组在某种程度上很常见,具有基本 int 的路径也将是一个微优化。它只是属于速度咆哮:https://ericlippert.com/2012/12/17/performance-rant/
如果你有两个函数重载——一个是int
,一个是int[]
——你应该只编码出int[]
版本。链接 int 版本来调用数组版本非常简单。
【讨论】:
“有时它是 1 的奇怪情况,没什么好担心的” 我的不安源于这样一个事实,即大约一半的 ItemHolders 将有一个长度为 1 的数组,因此总共有数百个。无论如何,我有点失望,无法进一步和更详细地确定开销 @harlekintiger 再次阅读速度咆哮。差异非常小,即使没有优化。很可能甚至是毫无意义的小。您对数百个元素执行 array access* 可能并不重要。但是您对**数百个元素进行数组访问。 |如果有任何严重的性能影响,那就是需要处理的事情的绝对数量。如果处理其中一个需要 110 或 100 个 CPU 周期,则不会。 @harlekintiger 请注意,如果您确实需要这种差异,那么您已经深入实时编程领域。如果是这样的话,开始使用 C#/.NET 是个坏主意。具有 GC 内存管理和 JiT 编译器的运行时不是启动任何实时编程项目的正确环境。 看到我想到的所有解决方案基本上都是O(n),我不得不承认性能上的差异不会很明显。我问的原因是 50% 是出于对幕后实现细节的好奇,还有 50% 是想了解此类任务的最佳实践 @harlekintiger 最佳实践?编写一个有效的程序。如果它足够快,你就完成了。如果还不够快?你需要找到一匹足够快的马。而这个只会稍微快一点,所以可能不够快。如果您需要这种速度,Multitreading 可能值得一看。以上是关于C# 长度为 1 的数组与单值、性能和内存开销的主要内容,如果未能解决你的问题,请参考以下文章