0

我有一些处理大量复制数组的代码。基本上我的类是一个使用数组作为支持字段的集合,并且由于我不想冒任何人修改现有集合的风险,因此大多数操作都涉及在修改集合之前创建集合的副本,因此也复制支持数组。

我注意到复制有时会很慢,在可接受的范围内,但我担心当应用程序扩大并开始使用更多数据时这可能是一个问题。

一些性能分析测试表明,虽然几乎没有消耗 CPU 资源,但我的数组复制代码花费了大量时间阻塞。很少有争执,但很多时间都被阻塞了。由于测试应用程序是单线程的,我假设有一些 GC 争用魔法正在发生。我对 GC 在这些场景中的工作方式没有足够的信心,所以我在这里问。

我的问题 - 有没有办法创建新的阵列来减少 GC 的压力?或者有没有其他方法可以加快速度(为了测试和可读性而简化):

public MyCollection(MyCollection copyFrom)
{

    _items = new KeyValuePair<T, double>[copyFrom._items.Length]; //this line is reported to have a lot of contention time

    Array.Copy(copyFrom._items, _items, copyFrom._items.Length);

    _numItems = copyFrom._numItems;

}
4

3 回答 3

3

不太清楚这里发生了什么,但争用是线程问题,而不是数组复制问题。是的,并发分析器很容易指向一个语句,因为内存分配需要获取一个保护堆的锁。

当分配来自 gen #0 堆时,该锁会保持很短的时间。因此,让线程争夺锁并浪费大量时间被锁定是不太可能发生的事故。当分配来自大对象堆时,它并没有那么快。当分配为 85,000 字节或更多时发生。但是,一个线程当然也会忙于复制数组元素。

请注意该工具告诉您的内容,大量的总争用并不自动意味着您有问题。只有当线程最终被阻塞很长时间时,它才会变得丑陋。如果是一个真正的问题,那么您接下来需要查看在垃圾收集上花费了多少时间。有一个基本的性能计数器,您可以在 Perfmon.exe 中看到它。类别“.NET CLR 内存”,计数器“% Time in GC”,实例 = yourapp. 考虑到您复制的数量,这可能会很高。如果是真正的问题,您可以调整的旋钮是启用服务器 GC

于 2014-11-12T09:50:07.110 回答
1

有一个持久不可变数据结构的概念。这是一种可能的解决方案,基本上可以让您以内存有效的方式创建不可变对象,同时仍然修改它们。

例如,

Roslyn 有一个 SyntaxTree 对象,它是不可变的。您可以修改不可变对象,并取回修改后的不可变对象。请注意,“修改后的不可变对象”可能没有内存分配,因为它可以建立在“第一个不可变对象”之上。

Visual Studio 文本编辑器本身也使用相同的概念。是不可变的TextBuffer对象,但是每次按下键盘按钮时,immutable TextBuffer都会创建新的,但是,它们不会分配内存(因为这会很慢)。

此外,如果您确实遇到了 LOH 问题,有时当您自己分配大内存块并将其用作“可重用”内存池时会有所帮助,从而完全避免 GC。值得考虑。

于 2014-11-12T09:35:39.830 回答
0

不。您可以等待 2015 年的新运行时,尽管这将使用 SIMD 指令进行 Array.Copy 操作。这会快很多。当前的实现非常不理想。

最后,诀窍在于避免内存操作——这有时是不可能的。

于 2014-11-12T08:57:52.827 回答