c# – 垃圾收集和Parallel.ForEach问题后VS2015升级

我有一些代码在我自己的R类C#DataFrame类中处理几百万个数据行。有许多Parallel.ForEach调用来并行地遍历数据行。此代码已运行一年多使用VS2013和.NET 4.5没有问题。

我有两台开发机(A和B),最近将机器A升级到VS2015。我开始注意到一个奇怪的间歇性冻结在我的代码约一半的时间。让它运行很长时间,结果证明代码最终完成。它只需要15-120分钟,而不是1-2分钟。

尝试使用VS2015调试器断开所有使用由于某种原因保持失败。所以我插入了一堆日志语句。事实证明,当在Parallel.ForEach循环(比较每个Parallel.ForEach循环之前和之后的集合计数)时有一个Gen2集合时,这种冻结发生。整个额外的13-118分钟用于任何Parallel.ForEach循环调用与Gen2集合(如果有)重叠。如果在任何Parallel.ForEach循环(大约50%的时间,当我运行)没有Gen2集合,那么一切都在1-2分钟内完成。

当我在VS2013上运行相同的代码在机器A,我得到相同的冻结。当我在VS2013上运行代码在机器B(从未升级),它的工作完美。它跑了几十个时间过夜,没有冻结。

有些事情我注意到/尝试:

>冻结发生有或没有调试器附加在机器A(我想它是与VS2015调试器在第一)
>无论我是在调试还是发布模式下,冻结发生
>如果我定位.NET 4.5或.NET 4.6,则会发生冻结
>我试图禁用RyuJIT,但这不影响冻结

我不改变默认的GC设置。根据GCSettings,所有运行都发生LatencyMode Interactive和IsServerGC为假。

我可以在每次调用Parallel.ForEach之前切换到LowLatency,但我真的更愿意了解发生了什么。

有没有人看到奇怪的冻结在Parallel.ForEach VS2015升级后?关于什么是好的下一步的任何想法?

更新1:添加一些示例代码到上面的模糊解释…

这里是一些示例代码,我希望将演示此问题。此代码在B机器上一直运行10-12秒。它遇到了一些Gen2集合,但他们几乎没有时间。如果我取消注释两个GC设置行,我可以强制它没有Gen2集合。它有点慢,然后在30-50秒。

现在在我的A机器上,代码需要随机的时间量。似乎在5和30分钟之间。它似乎越来越糟,更多的Gen2集合,它遇到。如果我取消注释两个GC设置行,机器A上也需要30-50秒(与机器B相同)。

它可能需要一些调整在行数和数组大小,以显示在另一台机器上。

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();
    }
}

更新2:只是为了未来的读者移动的意见…

此修补程序:https://support.microsoft.com/en-us/kb/3088957完全修复此问题。我在申请后根本看不到任何缓慢问题。

原来,没有与Parallel.ForEach任何事情相信基于这一点:http://blogs.msdn.com/b/maoni/archive/2015/08/12/gen2-free-list-changes-in-clr-4-6-gc.aspx虽然该修补程序确实提到Parallel.ForEach由于某种原因。

http://stackoverflow.com/questions/31747992/garbage-collection-and-parallel-foreach-issue-after-vs2015-upgrade

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c# – 垃圾收集和Parallel.ForEach问题后VS2015升级