我有一些代码可以在我自己的类 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 分钟都花在了 Parallel.ForEach 循环调用恰好与 Gen2 集合(如果有)重叠的情况下。如果在任何 Parallel.ForEach 循环期间没有 Gen2 集合(大约 50% 的时间我运行它),那么一切都会在 1-2 分钟内完成。
当我在机器 A 上的 VS2013 中运行相同的代码时,我得到了相同的卡住。当我在机器 B(从未升级过)上运行 VS2013 中的代码时,它运行良好。连夜跑了几十次,没有卡顿。
我注意到/尝试过的一些事情:
我根本没有更改默认的 GC 设置。根据 GCSettings,所有运行都在 LatencyMode Interactive 和 IsServerGC 为 false 的情况下发生。
我可以在每次调用 Parallel.ForEach 之前切换到 LowLatency,但我真的更愿意了解发生了什么。
有没有其他人在 VS2015 升级后看到 Parallel.ForEach 中出现奇怪的卡住?关于下一步的好的想法是什么?
更新 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:只是为了将评论中的内容移出以供 future 的读者使用...
此修补程序: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。
最佳答案
这确实表现得太差了,后台 GC 在这里对你不利。我注意到的第一件事是 Parallel.ForEach() 使用了太多任务。线程池管理器将线程行为误解为“因 I/O 而陷入困境”并启动额外的线程。这使问题变得更糟。解决方法是:
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
Parallel.ForEach(dataFrame, options, dr => {
// etc..
}
这可以让您更好地了解 VS2015 中新诊断中心的程序出了什么问题。只需一个 单个 内核即可完成任何工作,这很容易从 CPU 使用情况中看出。偶尔出现尖峰,它们不会持续很长时间,与橙色 GC 标记一致。当您仔细查看 GC 标记时,您会发现它是一个 gen #1 集合。花费了非常的时间,在我的机器上大约需要 6 秒。
第 1 代收集当然不会花那么长时间,您在这里看到的是第 1 代收集在等待后台 GC 完成其工作。换句话说,实际上是后台 GC 花费了 6 秒。仅当 gen #0 和 gen #1 段中的空间足够大以在后台 GC 运行时不需要 gen #2 收集时,后台 GC 才有效。不是这个应用程序的工作方式,它以非常高的速度消耗内存。您看到的小峰值是多个任务被解除阻塞,能够再次分配数组。当第 1 代收集必须再次等待后台 GC 时,快速停止。
值得注意的是这段代码的分配模式对GC非常不友好。它将长生命周期数组 (dr.DerivedValues) 与短生命周期数组 (tempArray) 交织在一起。在压缩堆时给 GC 大量工作,每个分配的数组最终都会被移动。
.NET 4.6 GC 中的明显缺陷是后台收集似乎从未有效地压缩堆。 看起来它一遍又一遍地完成这项工作,就好像上一个集合根本没有压缩一样。很难说这是设计使然还是错误,我再也没有干净的 4.5 机器了。我当然倾向于错误。您应该在 connect.microsoft.com 上报告此问题,以便 Microsoft 进行检查。
解决方法很容易找到,您所要做的就是防止长生命周期和短生命周期对象尴尬地交织在一起。您可以通过预先分配它们来实现:
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();
});
当然还有完全禁用后台 GC。
更新:在 this blog post 中确认了 GC 错误.即将修复。
更新:已在 .NET 4.6.1 中修复
关于c# - VS2015升级后的垃圾回收和Parallel.ForEach问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31747992/
在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我最近决定从我的系统中卸载RVM。在thispage提出的一些论点说服我:实际上,我的决定是,我根本不想担心Ruby的多个版本。我只想使用1.9.2-p290版本而不用担心其他任何事情。但是,当我在我的Mac上运行ruby--version时,它告诉我我的版本是1.8.7。我四处寻找如何简单地从我的Mac上卸载这个Ruby,但奇怪的是我没有找到任何东西。似乎唯一想卸载Ruby的人运行linux,而使用Mac的每个人都推荐RVM。如何从我的Mac上卸载Ruby1.8.7?我想升级到1.9.2-p290版本,并且我希望我的系统上只有一个版本。 最佳答案
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我最近与一位同事讨论了以下Ruby语法:value=ifa==0"foo"elsifa>42"bar"else"fizz"end我个人并没有看到太多这种逻辑,但我的同事指出,这实际上是一种相当普遍的Rubyism。我试着用谷歌搜索这个主题,但没有找到任何文章、页面或SO问题来讨论它,这让我相信这可能是一种非常实际的技术。然而,另一位同事发现语法令人困惑,而是将上面的逻辑写成这样:ifa==0value="foo"elsifa>42value="bar"elsevalue="fizz"end缺点是value=的重复声明和隐式elsenil的丢失,如果我们想使用它的话。这也感觉它与Ruby