代码之家  ›  专栏  ›  技术社区  ›  cdiggins

如何在高并发代码中提高.NET 4.0的垃圾收集器性能?

  •  12
  • cdiggins  · 技术社区  · 16 年前

    Parallel.For Parallel.ForEach

    例如,在这种情况下,有一些技术是有用的:

    • 我应该尝试手动管理GC吗?
    • 我应该用吗 Dispose
    • 我应该固定对象吗?

    问题不在于GC运行太频繁,而是GC阻止并发代码高效并行运行。我也不认为“分配更少的对象”是一个可以接受的答案。这就需要重写太多的代码来处理并行性差的垃圾收集器。

    我已经找到了一个有助于整体表现的技巧( using gcServer 平行的 在一个令人尴尬的并行任务中,只比串行For循环快20%。

    在对为什么任务并行库没有在这些任务的内核之间正确分配工作进行了大量调查之后,似乎罪魁祸首是GC。显然,GC似乎是一个瓶颈,因为它进行了一些我不理解的幕后线程同步。

    我需要知道的是:GC到底在做什么,当它执行大量分配时,会导致严重并发代码执行不良,以及我们如何解决这个问题 除了 只是 . 我已经想到了这种方法,需要对大量代码进行重大重写。

    8 回复  |  直到 16 年前
        1
  •  5
  •   Marek    16 年前

    如果由于分配的对象太多/GC-ed而导致GC运行过于频繁,请尝试分配较少的对象:)

    根据您的场景-尝试重用现有对象,创建对象池,使用不会造成太大内存压力的“更轻”对象(或更大的对象以减少分配的对象数量)。

    Rico Mariani says so )

    http://blogs.msdn.com/ricom/archive/2003/12/02/40780.aspx

        2
  •  2
  •   feal87    16 年前

    1) 您不能也不应该手动管理GC。

    避免这些问题的唯一方法是评测应用程序,并尽可能避免分配新对象。

    编辑: 每当GC运行时,所有线程都必须处于睡眠状态,以允许它执行其工作。如果收集的数量和您的情况一样多,那么这就是减速的原因。除了减少新对象的生成之外,没有其他方法可以管理此问题。

        3
  •  2
  •   Community Mohan Dere    9 年前

    关于你的四点:

    1. 看见 How can I improve garbage collector performance of .NET 4.0 in highly concurrent code? (1)
    2. 如果对象被传递给非托管代码,例如非托管C++ DLL,则只需将对象钉住。否则,让垃圾收集器在保持内存整洁方面尽自己的一份力量。固定也会导致内存碎片。
    3. 如果你不需要的话就不会。

    需要考虑的一件事是,如果可能的话,将分配移出循环。在许多情况下,当您能够做到这一点时,它还允许您重用已经分配的对象,从而提供额外的性能(至少我的经验显示了这一点)(另请参见 How can I improve garbage collector performance of .NET 4.0 in highly concurrent code? ).

    并行执行的级别始终取决于您正在执行的任务,在计算的情况下,可实现的最大并行度为<n次,其中n是处理器的数量-纯计算。在输入或输出操作的情况下,通常会超过n。

        4
  •  2
  •   Promit    16 年前

    我有一个想法--为什么不尝试另一种GC实现呢。NET提供了三个。

    http://blogs.msdn.com/maoni/archive/2004/09/25/234273.aspx

    根据您的问题描述,我很想了解服务器GC是如何为您工作的,因为它为每个核心提供了一个单独的堆。可能还值得研究.NET4添加的后台GC模式。

    http://blogs.msdn.com/maoni/archive/2008/11/19/so-what-s-new-in-the-clr-4-0-gc.aspx

    希望这比目前为止的答案对您的具体案例更有帮助。

        5
  •  1
  •   dsimcha    16 年前

    这是生活中的事实。几乎所有的内存管理方案都序列化了在某种程度上看起来令人尴尬的并行代码。我认为C#有线程本地分配器,所以它应该只在集合上序列化。尽管如此,我还是建议共享/重用最频繁分配的对象和数组,并可能将一些小的、非多态的对象转换为结构,看看这是否有帮助。

        6
  •  1
  •   Stack Overflow is garbage    16 年前

    在分析系统时,由于垃圾收集器的存在,看起来有很多线程同步正在进行。我正在进行大量的对象分配,因此我想知道如何在最大限度地减少代码重写的同时提高并发性。

    • 实现更好的GC,或者
    • 给GC更少的工作去做

    第一点几乎是不可能的。首先需要大量的黑客攻击才能取代.NET GC,而设计一个与.NET GC同样高效的GC也需要大量的工作。

    第二点实际上是您唯一的选择:如果垃圾收集需要同步,请确保进行较少的收集。它们通常发生在gen0堆太满而无法满足分配请求时。

    1. 使用(堆栈分配的)结构而不是类可能有助于降低GC压力。特别是小的、寿命短的对象可能会从转换为结构中受益,
        7
  •  1
  •   J D    15 年前

    当GC执行大量分配时,究竟是什么导致严重并发的代码执行不良

    但是,我不相信GC是本例中问题的根源。有许多方法可以在多核上实现较差的可扩展性。未能利用缓存是另一个常见的问题,它最终导致所有内核在访问共享内存时暂停,以几乎无法检测到的方式破坏了可伸缩性。。。

        8
  •  0
  •   Matthew Whited    16 年前

    并行任务甚至原始线程都不是使代码运行更快的灵丹妙药。如果您有任何锁、资源,或者只有几个内核,那么您可以减慢代码成为多线程的速度。您还需要确保您没有上下文交换,并且希望您有4个以上的内核(不要忘记GC、CLR、Windows以及其他应用程序和服务正在争夺资源/周期。)

    或者如果你是 不安全的

    并行任务库是为通用目的而创建的。如果您需要高度优化的代码,您可能还需要管理自己的线程(不像很多博客说的那样。。。在这个行业里没有什么灵丹妙药。)

    最好的办法是为每个线程创建一个worker类实例,以避免每个操作的构造和解构。退房 ThreadStaticAttribute . 据我所知,在.NET4.0中还有其他选项,但我还没有机会使用它们。