【问题标题】:TPL and memory managementTPL 和内存管理
【发布时间】:2021-01-23 03:51:24
【问题描述】:

使用Visual Studio Concurrency Visualizer 我现在明白为什么切换到Parallel.For 没有任何好处:只有 9% 的时间机器忙于执行代码,其余时间是 71% 的同步和 17% 的内存管理 (1)。

检查下图中的所有橙色条纹,我发现始终涉及 GC (2)。

阅读完所有这些有趣的话题后……

.. 我是否正确假设所有这些线程都需要使用单个内存管理对象,因此无需在堆上分配对象我的场景会大大改善?比如用结构体代替类、数组代替动态列表等?

我有很多工作要做,才能让我的代码朝着这个方向发展。只是想在开始之前确定一下。

【问题讨论】:

  • 介意分享一些代码吗?
  • 是的,分配较少可能会对您的资源和效率产生很大的好处,但这主要是总是热路径上的情况 和 thrashed 应用程序
  • 说这不是你可以用来减少应用程序分配的唯一工具。一个好的 memory profiler 将大有帮助,结合学习如何读取结果并根据结果影响更改。创建最小分配代码是一门艺术,值得你学习
  • 我明白了。那么迈克尔的 cmets 基本上就是你的答案。
  • Just wanted to be sure before starting. 简短回答 - 你不能 100% 确定。多线程代码需要基准测试 - 有一些技术倾向于运行良好 - 但不能保证。它们可能在某些机器和工作负载上运行良好,但在其他机器和工作负载上运行良好。基准测试将是必不可少的。

标签: c# .net multithreading performance locking


【解决方案1】:

从您的屏幕截图看来,内存分配在等待 GC 完成时被阻塞了。有服务器和工作站 GC 模式,可能是并发的,也可能不是,但所有选项都需要至少阻塞线程一段时间。我会更详细地检查您在 GC 上花费的频率和时间,以及 gen 0/1 和 2 的运行频率。

我相信每个线程都有一个单独的临时段用于分配,因此它不需要同步分配,除非它需要一个新段,或者分配在大对象堆上。但我找不到这方面的参考资料。

在任何情况下,您都可能会从减少分配的数量和大小中受益。如果可能,使用对象池或内存池来重用内存。您还可以从增加内存量和检查应用程序是否存在内存泄漏中受益。内存的一般建议是应该有两种类型的分配:

  1. 仅在短时间内存在的小型临时分配,例如在方法调用期间存在的临时对象。
  2. 在“应用程序”期间有效的任何大小的长期分配。

如果遵循这种模式,几乎所有垃圾都应该在第 0/1 代收集,而第 2 代收集应该相当少。

如果您要分配许多小对象或大块内存,这也取决于您。如果是前者,您可以考虑使用结构,因为它们是堆栈分配的。如果是后者,您还需要考虑内存碎片,这也应该通过使用仅分配固定大小的内存块的内存池来改善。

编辑:

最简单的对象池可能是这样的:

public class ObjectPool<T> 
{
    private ConcurrentBag<T> pool = new ConcurrentBag<T>();
    public T Get(Func<T> constructor) => pool.TryTake(out var result) ? result : constructor();
    public void Return(T obj) => pool.Add(obj);
}

这假定对象代表相同的资源,例如某些固定大小的字节数组。但也有现有的实现:

【讨论】:

  • “使用对象池或内存池来重用内存”您的意思是this还是只是将收集容量设置为初始值?
  • @abenci 设置集合的初始容量也是一个好主意。但我更多地指的是链接之类的东西。添加了一些关于对象和内存池的细节。
【解决方案2】:

内存管理 内存管理报告显示发生内存管理阻塞的调用,以及总阻塞 每个调用堆栈的次数。使用此信息来确定区域 有过多的分页或垃圾收集问题。

更多

Memory management time

时间线中的这些片段与阻塞时间相关联 被归类为内存管理。这种情况意味着 线程被与内存关联的事件阻塞 Paging 等管理操作。在此期间,一个线程有 在并发可视化工具的 API 或内核状态中被阻止 算作内存管理。其中包括分页等事件 和内存分配。检查相关的调用堆栈和配置文件 报告以更好地了解阻止的根本原因 被归类为内存管理。

是的,分配较少可能会对您的资源和效率产生很大的好处,但 几乎总是热路径受到冲击的应用程序上 /em>

堆分配和特定的大对象堆 (LOB) 分配成本很高,它还会为您的垃圾收集器创建额外的工作,并且可能会使您的内存碎片化,从而导致效率更低。您分配、重用内存或使用堆栈的越少(通常)越好。

您还可以在这里学习使用良好的内存分析器并了解您的垃圾收集器。

说这不是您用来减少应用程序分配的唯一工具。一个好的内存分析器将大有帮助,结合学习如何读取结果并根据结果影响更改。

创建最小分配代码是一门艺术,值得你学习

正如@mjwills 在 cmets 中指出的那样,您也可以通过您的基准测试软件运行任何更改,以 CPU 时间为代价删除分配是没有意义的。有很多方法可以加快代码速度,而低分配只是众多可能有帮助的方法之一。

最后,我建议首先关注 Marc Gravell 和他的博客(DeAllocation 先生),了解您的 Garbage Collector 以及世代如何工作,以及 内存分析器等工具 和性能丝滑生产代码的基准测试

【讨论】:

  • 谢谢! 您分配、重用内存或使用堆栈的次数越少(通常情况下)就越好:71% 的同步时间也会从中受益吗?
  • @abenci 很可能不是。减少分配不太可能对同步开销产生任何影响。因此,随着总时间的减少,同步百分比会上升。要减少同步时间,您必须改进同步代码,尽量减少临界区,或者尽可能完全消除它们。
  • @Theodor:嗯,我的代码中没有关键部分...
  • @Michael:我找不到您提供的参考资料,例如“了解您的垃圾收集器”,您可以添加链接吗?谢谢。
  • @abenci 抱歉,我是先入为主的。没有这样的参考资料,只是一般性的评论,关于 GC 及其工作原理有很多很好的资源。
猜你喜欢
  • 1970-01-01
  • 2016-10-12
  • 2013-01-17
  • 2011-02-18
  • 2011-07-01
  • 2019-11-10
  • 2011-02-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多