C# TPL与内存管理

C# TPL与内存管理,c#,.net,multithreading,performance,locking,C#,.net,Multithreading,Performance,Locking,使用“我现在明白了为什么切换到并行模式没有任何好处。对于:只有9%的时间机器忙于执行代码,其余时间是71%的同步和17%的内存管理(1) 检查下图中所有的橙色条纹,我发现GC总是涉及(2) 读完所有这些有趣的话题 。。假设所有这些线程都需要使用一个内存管理对象,因此不再需要在堆上分配对象,我的场景会有很大的改进,这是对的吗?比如用结构代替类,用数组代替动态列表等等 我有很多工作要做,以使我的代码朝这个方向弯曲。只是想在开始之前确定一下 内存管理内存管理报告显示内存管理块发生

使用“我现在明白了为什么切换到
并行模式没有任何好处。对于
:只有9%的时间机器忙于执行代码,其余时间是71%的同步和17%的内存管理(1)

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

读完所有这些有趣的话题

。。假设所有这些线程都需要使用一个内存管理对象,因此不再需要在堆上分配对象,我的场景会有很大的改进,这是对的吗?比如用结构代替类,用数组代替动态列表等等

我有很多工作要做,以使我的代码朝这个方向弯曲。只是想在开始之前确定一下

内存管理内存管理报告显示内存管理块发生的调用以及总阻塞 每个调用堆栈的时间。使用此信息可确定需要 有过多的分页或垃圾收集问题

更多信息

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

是的,分配更少的资源可能会对您的资源和效率带来很大的好处,但在热门路径和受重创的应用程序上几乎总是这样

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

在这里,您还将学习使用一个好的内存分析器,并了解您的垃圾收集器


他说,这并不是使应用程序减少分配的唯一工具。一个好的记忆体剖析器将有很大的帮助,它与学习如何读取结果以及如何根据结果影响更改相结合

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

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


最后,我建议从以下和他的博客开始(Mr DeAllocation),了解您的垃圾收集器以及各代如何工作,以及内存分析器和基准测试工具等工具如何实现流畅流畅的生产代码从您的屏幕截图上看,在等待GC完成时,内存分配似乎被阻止。有服务器和工作站GC模式,它可能是并发的,也可能不是,但所有选项都需要阻塞线程至少一段时间。我将更详细地检查GC的运行频率和时间,以及gen 0/1和gen 2的运行频率

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

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

  • 仅在短时间内有效的小型临时分配,如在方法调用期间有效的临时对象
  • 在“应用程序”期间有效的任何大小的长期分配
  • 如果遵循此模式,则几乎所有垃圾都应该在Gen 0/1中收集,Gen 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);
    }
    
    公共类对象池
    {
    私有ConcurrentBag池=新ConcurrentBag();
    public T Get(Func构造函数)=>pool.TryTake(out var result)?结果:构造函数();
    公共无效返回(T obj)=>pool.Add(obj);
    }
    
    这假设对象表示相同的资源,比如固定大小的字节数组。但也有一些现有的实现:


    介意分享一些代码吗?是的,分配较少的资源可能会对您的资源和效率产生很大的好处,但在热门路径和备受打击的应用程序上通常都是这样的,您可以说,这并不是使您的应用程序分配较少的唯一工具。一个好的记忆体剖析器将有很大的帮助,它与学习如何读取结果以及如何根据结果影响更改相结合。创建最小分配代码是一门艺术,我明白了,它值得你学习。那么迈克尔的评论基本上就是你