C# VS2015升级后的垃圾收集和Parallel.ForEach问题
我有一些代码,可以在我自己的类似R的C#DataFrame类中处理数百万个数据行。有许多Parallel.ForEach调用用于并行迭代数据行。这段代码已经使用VS2013和.NET 4.5运行了一年多,没有任何问题 我有两台开发机器(A和B),最近将机器A升级到VS2015。大约有一半的时间,我开始注意到我的代码中有一个奇怪的间歇性冻结。让它运行很长一段时间,结果证明代码最终完成了。它只需要15-120分钟,而不是1-2分钟 由于某些原因,尝试使用VS2015调试器中断所有调试都会失败。所以我插入了一堆日志语句。事实证明,在Parallel.ForEach循环期间存在Gen2集合时(比较每个Parallel.ForEach循环前后的集合计数),会发生此冻结。所有额外的13-118分钟都花在任何并行中。ForEach循环调用恰好与Gen2集合(如果有)重叠。如果在任何Parallel.ForEach循环过程中都没有Gen2集合(我运行它时大约有50%的时间),那么一切都会在1-2分钟内完成 当我在机器A上运行VS2013中的相同代码时,我得到了相同的冻结。当我在机器B(从未升级)上运行VS2013中的代码时,它工作得非常好。它在一夜之间运行了几十次,没有结冰 我注意到/尝试过的一些事情:C# VS2015升级后的垃圾收集和Parallel.ForEach问题,c#,garbage-collection,visual-studio-2015,parallel.foreach,.net-4.6,C#,Garbage Collection,Visual Studio 2015,Parallel.foreach,.net 4.6,我有一些代码,可以在我自己的类似R的C#DataFrame类中处理数百万个数据行。有许多Parallel.ForEach调用用于并行迭代数据行。这段代码已经使用VS2013和.NET 4.5运行了一年多,没有任何问题 我有两台开发机器(A和B),最近将机器A升级到VS2015。大约有一半的时间,我开始注意到我的代码中有一个奇怪的间歇性冻结。让它运行很长一段时间,结果证明代码最终完成了。它只需要15-120分钟,而不是1-2分钟 由于某些原因,尝试使用VS2015调试器中断所有调试都会失败。所以我
- 冻结是在机器A上附加或不附加调试器的情况下发生的(起初我认为这是VS2015调试器的问题)
- 无论我是在调试模式还是发布模式下构建,冻结都会发生
- 如果我以.NET4.5或.NET4.6为目标,就会发生冻结
- 我试着禁用龙井,但这并没有影响冻结
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Runtime;
public class MyDataRow
{
public int Id { get; set; }
public double Value { get; set; }
public double DerivedValuesSum { get; set; }
public double[] DerivedValues { get; set; }
}
class Program
{
static void Example()
{
const int numRows = 2000000;
const int tempArraySize = 250;
var r = new Random();
var dataFrame = new List<MyDataRow>(numRows);
for (int i = 0; i < numRows; i++) dataFrame.Add(new MyDataRow { Id = i, Value = r.NextDouble() });
Stopwatch stw = Stopwatch.StartNew();
int gcs0Initial = GC.CollectionCount(0);
int gcs1Initial = GC.CollectionCount(1);
int gcs2Initial = GC.CollectionCount(2);
//GCSettings.LatencyMode = GCLatencyMode.LowLatency;
Parallel.ForEach(dataFrame, dr =>
{
double[] tempArray = new double[tempArraySize];
for (int j = 0; j < tempArraySize; j++) tempArray[j] = Math.Pow(dr.Value, j);
dr.DerivedValuesSum = tempArray.Sum();
dr.DerivedValues = tempArray.ToArray();
});
int gcs0Final = GC.CollectionCount(0);
int gcs1Final = GC.CollectionCount(1);
int gcs2Final = GC.CollectionCount(2);
stw.Stop();
//GCSettings.LatencyMode = GCLatencyMode.Interactive;
Console.Out.WriteLine("ElapsedTime = {0} Seconds ({1} Minutes)", stw.Elapsed.TotalSeconds, stw.Elapsed.TotalMinutes);
Console.Out.WriteLine("Gcs0 = {0} = {1} - {2}", gcs0Final - gcs0Initial, gcs0Final, gcs0Initial);
Console.Out.WriteLine("Gcs1 = {0} = {1} - {2}", gcs1Final - gcs1Initial, gcs1Final, gcs1Initial);
Console.Out.WriteLine("Gcs2 = {0} = {1} - {2}", gcs2Final - gcs2Initial, gcs2Final, gcs2Initial);
Console.Out.WriteLine("Press Any Key To Exit...");
Console.In.ReadLine();
}
static void Main(string[] args)
{
Example();
}
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
使用System.IO;
使用系统诊断;
使用系统线程;
使用System.Threading.Tasks;
使用System.Linq;
使用系统运行时;
公共类MyDataRow
{
公共int Id{get;set;}
公共双值{get;set;}
公共双派生值sum{get;set;}
public double[]DerivedValues{get;set;}
}
班级计划
{
静态void示例()
{
常数int numRows=2000000;
常数int tempArraySize=250;
var r=新的随机变量();
var dataFrame=新列表(numRows);
对于(inti=0;i
{
double[]tempArray=新的double[tempArraySize];
对于(int j=0;j
更新2:只是为了将评论中的内容转移给未来的读者…
这个修补程序:完全解决了这个问题。在申请之后,我没有发现任何缓慢的问题
事实证明,它与Parallel.ForEach没有任何关系,我相信这是基于这样的原因:尽管修补程序确实出于某种原因提到了Parallel.ForEach。这确实执行得非常糟糕,但后台GC在这里并没有给您带来好处。我注意到的第一件事是Parallel.ForEach()使用了太多的任务。线程池管理器将线程行为误解为“被I/O困住”,并启动额外的线程。这使问题变得更糟。这方面的解决办法是:
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
Parallel.ForEach(dataFrame, options, dr => {
// etc..
}
这使我们能够更好地了解什么是疾病
for (int i = 0; i < numRows; i++) dataFrame.Add(new MyDataRow {
Id = i, Value = r.NextDouble(),
DerivedValues = new double[tempArraySize] });
...
Parallel.ForEach(dataFrame, options, dr => {
var array = dr.DerivedValues;
for (int j = 0; j < array.Length; j++) array[j] = Math.Pow(dr.Value, j);
dr.DerivedValuesSum = array.Sum();
});
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<runtime>
<gcConcurrent enabled="false" />
</runtime>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<runtime>
<gcServer enabled="true" />
</runtime>
</configuration>