- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我目前正在测试Parallel for C#。通常,它运行良好,并且使用并行比正常的foreach循环要快。但是,有时(例如,五分之一),我的CPU使用率将达到100%,从而导致并行任务非常缓慢。我的CPU设置是8GB内存的i5-4570。有谁知道为什么会出现此问题?
以下是我用来测试该功能的代码
// Using normal foreach
ConcurrentBag<int> resultData = new ConcurrentBag<int>();
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (var item in testData)
{
if (item.Equals(1))
{
resultData.Add(item);
}
}
Console.WriteLine("Normal ForEach " + sw.ElapsedMilliseconds);
// Using list parallel for
resultData = new ConcurrentBag<int>();
sw.Restart();
System.Threading.Tasks.Parallel.For(0, testData.Count() - 1, (i, loopState) =>
{
int data = testData[i];
if (data.Equals(1))
{
resultData.Add(data);
}
});
Console.WriteLine("List Parallel For " + sw.ElapsedMilliseconds);
// Using list parallel foreach
//resultData.Clear();
resultData = new ConcurrentBag<int>();
sw.Restart();
System.Threading.Tasks.Parallel.ForEach(testData, (item, loopState) =>
{
if (item.Equals(1))
{
resultData.Add(item);
}
});
Console.WriteLine("List Parallel ForEach " + sw.ElapsedMilliseconds);
// Using concurrent parallel for
ConcurrentStack<int> resultData2 = new ConcurrentStack<int>();
sw.Restart();
System.Threading.Tasks.Parallel.For(0, testData.Count() - 1, (i, loopState) =>
{
int data = testData[i];
if (data.Equals(1))
{
resultData2.Push(data);
}
});
Console.WriteLine("Concurrent Parallel For " + sw.ElapsedMilliseconds);
// Using concurrent parallel foreach
resultData2.Clear();
sw.Restart();
System.Threading.Tasks.Parallel.ForEach(testData, (item, loopState) =>
{
if (item.Equals(1))
{
resultData2.Push(item);
}
});
Console.WriteLine("Concurrent Parallel ForEach " + sw.ElapsedMilliseconds);
var resultData3 = testData.AsParallel().Where(x => x == 1).ToList();
最佳答案
首先,请小心Parallel
-它不会使您免受线程安全问题的困扰。在原始代码中,填写结果列表时使用了非线程安全的代码。通常,您要避免共享任何状态(尽管在这种情况下,对列表的只读访问权限很好)。如果您确实要使用Parallel.For
或Parallel.ForEach
进行过滤和聚合(确实,在这些情况下,AsParallel
是您想要的),则应使用具有线程本地状态的重载-您将得到最终结果localFinally
委托中的聚合(请注意,它仍在不同的线程上运行,因此您需要确保线程安全;但是,在这种情况下,锁定是可以的,因为您只对每个线程执行一次,而不是对每个线程执行一次每次迭代)。
现在,要尝试解决此类问题,显而易见的第一件事就是使用探查器。所以我做到了。结果如下:
在这些解决方案中,几乎没有任何内存分配。即使对于相对较小的测试数据(我在测试时使用1M,10M和100M的整数),它们与初始测试数据分配相比完全相形见war。
正在进行的工作例如Parallel.For
或Parallel.ForEach
主体本身,而不在您的代码中(简单的if (data[i] == 1) results.Add(data[i])
)。
第一种方法可以说GC可能不是罪魁祸首。确实,它没有任何运行的机会。第二个更加好奇-这意味着在某些情况下Parallel
的开销是不合时宜的-但它似乎是随机的,有时可以顺利运行,有时需要半秒钟。这通常指向GC,但是我们已经排除了这一点。
我试过使用没有循环状态的重载,但这没有帮助。我曾尝试限制MaxDegreeOfParallelism
,但它只会伤人。现在,显然,这段代码绝对由缓存访问控制-几乎没有CPU工作,也没有I / O-这将始终支持单线程解决方案;但是即使使用1的MaxDegreeOfParallelism
也无济于事-实际上,2似乎是我系统上最快的。更多是无用的-再次,缓存访问占主导。仍然很好奇-我正在使用服务器CPU进行测试,它一次可以为所有数据提供足够的缓存,而我们没有进行100%的顺序访问(这几乎完全消除了延迟) ),它应该足够连续。无论如何,我们在单线程解决方案中拥有内存吞吐量的基准线,并且它在运行良好的情况下非常接近并行化情况的速度(并行化时,我在单线程上读取的运行时间比单线程少40%。四核服务器CPU遇到了令人尴尬的并行问题-显然,内存访问是极限)。
因此,现在该检查参考源的Parallel.For
。在这种情况下,它只是根据工作人员的数量创建范围-每个范围一个。因此,这不是范围-没有任何开销。
核心只是运行在给定范围内迭代的任务。有一些有趣的地方-例如,如果任务花费的时间太长,它将被“挂起”。但是,它似乎不太适合数据-为什么这样的事情会导致与数据大小无关的随机延迟?无论工作量多么小,以及MaxDegreeOfParallelism
多么低,我们都会得到“随机”的减速。这可能是个问题,但我不知道如何检查。
最有趣的是,扩展测试数据不会对异常产生任何影响-虽然它使“好”并行运行得更快(即使在我的测试中接近完美的效率,也很奇怪),但“坏”并行仍然只是一样糟糕。实际上,在我的一些测试运行中,它们非常糟糕(高达“正常”循环的十倍)。
因此,让我们看一下线程。我故意提高了ThreadPool
中的线程数量,以确保扩展线程池不会成为瓶颈(如果一切正常的话应该不会,但是...)。这是第一个惊喜-尽管“好”运行只使用有意义的4-8线程,但“坏”运行会扩展池中所有可用的线程,即使它们有一百个。哎呀
让我们再次深入源代码。 Parallel
在内部使用Task.RunSynchronously
运行根分区工作,然后在结果上使用Wait
。当我查看并行堆栈时,有97个线程在执行循环体,只有一个实际在堆栈上具有RunSynchronously
的线程(如预期的那样-这是主线程)。其他是普通线程池线程。任务ID还会讲述一个故事-进行迭代时会创建成千上万的单独任务。显然,这里有些错误。即使我删除了整个循环主体,这种情况仍然会发生,因此也不是某种闭包怪异。
显式设置MaxDegreeOfParallelism
可以在某种程度上抵消这一点-使用的线程数量不再爆炸-但是,任务数量仍然可以。但是我们已经看到范围只是运行的并行任务的数量-那么为什么要继续创建越来越多的任务呢?使用调试器可以确认这一点-MaxDOP为4,只有五个范围(某些对齐导致第五个范围)。有趣的是,其中一个已完成的范围(第一个完成得比其余的提前了多少?)的索引高于其迭代的范围-这是因为“调度程序”在最多16个切片中分配了范围分区。
根任务是自我复制的,因此无需显式启动例如四个任务处理数据,它等待调度程序复制任务以处理更多数据。这很难读-我们正在谈论复杂的多线程无锁代码,但似乎总是在比分区范围小得多的切片中分配工作。在我的测试中,切片的最大大小为16,这与我正在运行的数百万个数据相去甚远。用这样的主体进行16次迭代完全没有时间,这可能会导致算法出现许多问题(最大的问题是基础结构比实际的迭代器主体需要更多的CPU工作)。在某些情况下,缓存垃圾回收可能会进一步影响性能(也许在主体运行时有很多差异时),但是在大多数情况下,访问是足够连续的。
TL;博士
如果每次工作很短(毫秒级),请不要使用Parallel.For
和Parallel.ForEach
。 AsParallel
或仅运行单线程迭代将可能更快。
稍长的解释:
看来Parallel.For
和Paraller.ForEach
是为要迭代的单个项目花费大量时间来执行的方案而设计的(即,每个项目需要大量工作,而不是许多项目需要少量工作) 。当迭代器主体太短时,它们的性能似乎很差。如果您不在迭代器主体中做大量工作,请使用AsParallel
而不是Parallel.*
。最有效点似乎在每片150毫秒以下(每次迭代约10毫秒)。否则,Parallel.*
会在自己的代码中花费大量时间,几乎不会花时间进行迭代(在我的情况下,通常的数字大约是体内的5-10%,非常糟糕)。
令人遗憾的是,我在MSDN上没有发现任何警告-甚至有样本正在处理大量数据,但是并没有暗示这样做会带来可怕的性能损失。在我的计算机上测试了相同的示例代码,我发现它的确确实通常比单线程迭代慢,并且在最佳情况下,几乎没有快(在四个CPU内核上运行时可节省30-40%的时间) -效率不高)。
编辑:
Willaien在MSDN上发现了有关此问题以及如何解决它的问题-https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx。想法是使用自定义分区程序并在Parallel.For
主体中对其进行迭代(例如,Parallel.For
循环中的循环)。但是,在大多数情况下,使用AsParallel
可能仍然是一个更好的选择-简单的循环体通常意味着某种map / reduce操作,而AsParallel
和LINQ通常很适合这样做。例如,您的示例代码可以简单地重写为:
var result = testData.AsParallel().Where(i => i == 1).ToList();
AsParallel
是一个不好的主意的唯一情况是与所有其他LINQ相同-当循环主体具有副作用时。有些可能是可以忍受的,但完全避免它们是更安全的。
关于c# - 任务并行不稳定,有时使用100%CPU,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32041631/
猫f1.txt阿曼维沙尔阿杰贾伊维杰拉胡尔曼尼什肖比特批评塔夫林现在输出应该符合上面给定的条件 最佳答案 您可以在文件读取循环中设置一个计数器并打印它, 计数=0 读取行时做 让我们数一数++ if
我正在尝试查找文件 1 和文件 2 中的共同行。如果公共(public)行存在,我想写入文件 2 中的行,否则打印文件 1 中的非公共(public)行。fin1 和 fin2 是这里的文件句柄。它读
我有这个 SQL 脚本: CREATE TABLE `table_1` ( `IDTable_1` int(11) NOT NULL, PRIMARY KEY (`IDTable_1`) );
我有 512 行要插入到数据库中。我想知道提交多个插入内容是否比提交一个大插入内容有任何优势。例如 1x 512 行插入 -- INSERT INTO mydb.mytable (id, phonen
如何从用户中选择user_id,SUB(row, row - 1),其中user_id=@userid我的表用户,id 为 1、3、4、10、11、23...(不是++) --id---------u
我曾尝试四处寻找解决此问题的最佳方法,但我找不到此类问题的任何先前示例。 我正在构建一个基于超本地化的互联网购物中心,该区域分为大约 3000 个区域。每个区域包含大约 300 个项目。它们是相似的项
preg_match('|phpVersion = (.*)\n|',$wampConfFileContents,$result); $phpVersion = str_replace('"','',
我正在尝试创建一个正则表达式,使用“搜索并替换全部”删除 200 个 txt 文件的第一行和最后 10 行 我尝试 (\s*^(\h*\S.*)){10} 删除包含的前 10 行空白,但效果不佳。 最
下面的代码从数据库中获取我需要的信息,但没有打印出所有信息。首先,我知道它从表中获取了所有正确的信息,因为我已经在 sql Developer 中尝试过查询。 public static void m
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我试图在两个表中插入记录,但出现异常。您能帮我解决这个问题吗? 首先我尝试了下面的代码。 await _testRepository.InsertAsync(test); await _xyzRepo
这个基本的 bootstrap CSS 显示 1 行 4 列: Text Text Text
如果我想从表中检索前 10 行,我将使用以下代码: SELECT * FROM Persons LIMIT 10 我想知道的是如何检索前 10 个结果之后的 10 个结果。 如果我在下面执行这段代码,
今天我开始使用 JexcelApi 并遇到了这个:当您尝试从特定位置获取元素时,不是像您通常期望的那样使用sheet.getCell(row,col),而是使用sheet.getCell(col,ro
我正在尝试在我的网站上开发一个用户个人资料系统,其中包含用户之前发布的 3 个帖子。我可以让它选择前 3 条记录,但它只会显示其中一条。我是不是因为凌晨 2 点就想编码而变得愚蠢? query($q)
我在互联网上寻找答案,但找不到任何答案。 (我可能问错了?)我有一个看起来像这样的表: 我一直在使用查询: SELECT title, date, SUM(money) FROM payments W
我有以下查询,我想从数据库中获取 100 个项目,但 host_id 多次出现在 urls 表中,我想每个 host_id 从该表中最多获取 10 个唯一行。 select * from urls j
我的数据库表中有超过 500 行具有特定日期。 查询特定日期的行。 select * from msgtable where cdate='18/07/2012' 这将返回 500 行。 如何逐行查询
我想使用 sed 从某一行开始打印 n 行、跳过 n 行、打印 n 行等,直到文本文件结束。例如在第 4 行声明,打印 5-9,跳过 10-14,打印 15-19 等 来自文件 1 2 3 4 5 6
我目前正在执行验证过程来检查用户的旧密码,但问题是我无法理解为什么我的查询返回零行,而预期它有 1 行。另一件事是,即使我不将密码文本转换为 md5,哈希密码仍然得到正确的答案,但我不知道为什么会发生
我是一名优秀的程序员,十分优秀!