【问题标题】:Prevent GC Collections In Certain Spots To Improve Performance防止在某些地点进行 GC 收集以提高性能
【发布时间】:2015-07-22 10:26:50
【问题描述】:

我正在运行蒙特卡罗模拟。 工作分配给许多不同的机器(通常大约 150 台)。

在每次迭代之后,每个工作人员将其结果发送到服务器。 从所有工作人员那里获得结果后,服务器计算更新并将其发送回所有工作人员。

此循环重复 100-1000 次迭代。

在所有工作人员发送结果之前,服务器无法计算更新,因此如果 99 个工作人员需要 1 秒来完成一次迭代,而第 100 个工作人员需要 10 秒,那么整个迭代需要 10 秒。

问题是 GC 在某些迭代中随机启动一些工作人员,因此导致这些工作人员花费更多时间,从而减慢整个过程。

例如,在迭代 #1 中,worker #58 耗时 10 秒,而其他 worker 耗时 8 秒。在第 2 次迭代中,不同的工作人员需要更长的时间等等。

这增加的开销似乎在 20-30% 左右。

我想做的是指示 GC 在迭代发生时不要进行任何收集。 每隔 10 次迭代收集一次(以便所有工作人员同步他们的收集),或者在发送结果后收集,然后再从服务器获取更新。

这是我正在尝试做的伪代码:

public void Algorithm()
{
  for (var iteration = 0; iteration < 1000; iteration++)
  {
     PerformIteration(); //don't do any GC inside.
     SendResults();
     //Now there is a small time window to perform GC
     //before results from the server arrive (thats usually sub 0.5sec window)
     WaitForUpdate();
  }
}

设置:GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency 帮助很大,但仍然存在大量开销。

每个工人都有 244GB 的内存,远远超过模拟所需的内存。 此外,几乎所有内容都已缓存,因此无需进行 Gen2 集合。

【问题讨论】:

  • 你可以用 unmanged c++ 编写关键部分
  • 你解决了错误的问题。没有任何明显的需要等待所有机器完成它们的工作。当机器提供结果时,只需再打包一个。
  • @HansPassant 服务器必须等待所有工作人员计算“更新”,然后工作人员在下一次迭代中使用该更新。这不是某种工作队列,服务器在工作项可用时将工作项提供给它们。发生的情况是有一个巨大的树要遍历和更新,并且工人有特定的子树分配给遍历。然后服务器更新该树中的一些顶级节点并将结果发送给需要它正确执行下一次迭代的工作人员。
  • 嗯,这没有多大意义。在您的问题中描述如果您只有 50 台机器可用,您会做什么。或者你用 150 台机器做的事情是树的 3 倍大。
  • @HansPassant 想象一棵树,它的根有 N 个子树(即根有 N 个子树)。有 M 个工人。工作被划分,以便每个工人得到大约 N/M 个子树来工作。现在一个常见的情况是有 338 个子树和 169 个工人,所以他们每个人正好有 2 个子树。在这种情况下,如果我只使用 168 个工作人员,那么其中一个工作人员至少需要处理 3 个子树,从而将速度降低 33%。这些子树通常具有相似的大小。如果子树大 3 倍,那么模拟将花费 3 倍的时间。

标签: c# .net performance garbage-collection


【解决方案1】:

.NET 4.6 有一个名为 GC.TryStartNoGCRegion 的新 GC 功能。

这告诉 GC 尝试运行这段代码而不进行任何收集:

在执行过程中尝试禁止垃圾收集 如果指定数量的内存可用,则为关键路径,并且 控制垃圾收集器是否进行完全阻塞垃圾 如果最初没有足够的内存可用,则收集。

当您调用它时,您会指示 GC 在执行 GC 之前可以分配多少内存。它必须小于或等于临时段大小:

public void Algorithm()
{
   for (var iteration = 0; iteration < 1000; iteration++)
   {
        // allow the GC to allocate 4kb
        if (GC.TryStartNoGCRegion(4096, true))
        {
            try
            {
                PerformIteration();
                SendResults();
            }
            finally
            {
                GC.EndNoGCRegion();
            }
        }

      //Now there is a small time window to perform GC
      //before results from the server arrive (thats usually sub 0.5sec window)
      WaitForUpdate();
   }
}

【讨论】:

  • 尝试过类似的方法,但我认为我以错误的方式使用它并且 GC.TryStartNoGCRegion 正在挂起应用程序!我会试试你提出的解决方案,看看它是如何工作的。
【解决方案2】:

-您可以在 C# 上使用 unmanaged 代码与this (GC.AddMemoryPressure)

-或使用托管 GC 块

GC.TryStartNoGCRegion(.)
{
   // your critical code
} GC.EndNoGCRegion()

*不要忘记检查上一个答案的尝试条件

-GCSettings.LatencyModeMS manual

-使用gcServergcConcurrent 参数更改*.config 文件(应用程序或机器)中的GC 配置

<configuration>
    <runtime>
        <gcServer enabled="true"/>
<!-- OR / AND -->
        <gcConcurrent enabled="true|false"/>
    </runtime>
</configuration>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 1970-01-01
    相关资源
    最近更新 更多