我正在构建一个简单的LinQ-to-object查询,我想并行化,但是我想知道语句的顺序是否重要?
例如
IList<RepeaterItem> items;
var result = items
.Select(item => item.FindControl("somecontrol"))
.Where(ctrl => SomeCheck(ctrl))
.AsParallel();
Run Code Online (Sandbox Code Playgroud)
与
var result = items
.AsParallel()
.Select(item => item.FindControl("somecontrol"))
.Where(ctrl => SomeCheck(ctrl));
Run Code Online (Sandbox Code Playgroud)
会有什么不同吗?
一段时间以来,我一直围绕没有副作用的方法构建我的代码,以便使用并行linq来加快速度.一路走来,我不止一次偶然发现懒惰的评估让事情变得更糟而不是更好,我想知道是否有任何工具可以帮助优化并行linq查询.
我问,因为我最近通过修改某些方法并AsParallel在某些关键位置加油来重构一些令人尴尬的并行代码.运行时间从2分钟下降到45秒,但从性能监视器可以清楚地看出,有些地方CPU上的所有内核都没有得到充分利用.在一些错误的启动后,我强制执行一些查询,ToArray并且运行时间进一步降低到16秒.减少代码的运行时间感觉很好,但它也有点令人不安,因为不清楚需要强制使用代码查询的位置ToArray.等到查询执行的最后一分钟不是最佳策略,但是根本不清楚代码中的哪些点需要强制某些子查询才能利用所有CPU内核.
因为它是我不知道如何正确胡椒ToArray或其他方法迫使linq计算执行,以获得最大的CPU利用率.那么优化并行linq查询是否有任何通用指南和工具?
这是一个伪代码示例:
var firstQuery = someDictionary.SelectMany(FirstTransformation);
var secondQuery = firstQuery.Select(SecondTransformation);
var thirdQuery = secondQuery.Select(ThirdTransformation).Where(SomeConditionCheck);
var finalQuery = thirdQuery.Select(FinalTransformation).Where(x => x != null);
Run Code Online (Sandbox Code Playgroud)
FirstTransformation,SecondTransformation,ThirdTransformation都是CPU绑定,并且在复杂性方面,他们有几个3x3矩阵乘法和一些if分支机构.SomeConditionCheck几乎是一张null支票.FinalTransformation是代码中CPU密集度最高的部分,因为它将执行一大堆线平面交叉,并检查这些交叉点的多边形包含,然后提取最接近线上某个点的交点.
我不知道为什么我放置的地方AsParallel减少了代码的运行时间.我现在已经达到了运行时间的局部最小值,但我不知道为什么.我偶然发现它只是运气不好.如果你想知道放置的地方AsParallel是第一行和最后一行.放在AsParallel其他地方只会增加运行时间,有时甚至会增加20秒.ToArray第一行还有隐藏的藏身之处.
我想在一定程度的并行处理并行处理项目的同时对数据流执行查询.通常情况下,我会使用PLINQ,但我的工作项不是CPU绑定的,而是IO绑定的.我想使用异步IO.PLINQ不支持异步工作.
运行PLINQ样式查询的最聪明方法是什么,但使用异步工作项?
以下是该问题的更详细说明:
我的目标是以下面的查询逻辑描述的方式处理可能无限的"项目"流:
var items = new int[10]; //simulate data
var results =
from x in items.AsParallel().WithDegreeOfParallelism(100)
where Predicate(x)
select ComputeSomeValue(x);
foreach (var result in results)
PerformSomeAction(result);
Run Code Online (Sandbox Code Playgroud)
此查询只是真实查询的草图.现在我希望每个占位符函数都是异步的(返回a Task和内部基于异步IO).
请注意,可能存在的内容远远多于可以存储在内存中的项目.我还必须控制并行度以最大化底层网络和磁盘硬件.
这个问题不是关于多核的.它完全适用于只有一个CPU内核的机器,因为IO仍然可以从并行性中受益.想想慢速的Web服务调用等.
令人惊讶的是,使用PLINQ并没有为我创建的小测试案例带来好处; 事实上,它甚至比平时更糟糕的LINQ.
这是测试代码:
int repeatedCount = 10000000;
private void button1_Click(object sender, EventArgs e)
{
var currTime = DateTime.Now;
var strList = Enumerable.Repeat(10, repeatedCount);
var result = strList.AsParallel().Sum();
var currTime2 = DateTime.Now;
textBox1.Text = (currTime2.Ticks-currTime.Ticks).ToString();
}
private void button2_Click(object sender, EventArgs e)
{
var currTime = DateTime.Now;
var strList = Enumerable.Repeat(10, repeatedCount);
var result = strList.Sum();
var currTime2 = DateTime.Now;
textBox2.Text = (currTime2.Ticks - currTime.Ticks).ToString();
}
Run Code Online (Sandbox Code Playgroud)
结果?
textbox1: 3437500
textbox2: 781250
Run Code Online (Sandbox Code Playgroud)
因此,LINQ比PLINQ花费更少的时间来完成类似的操作!
我究竟做错了什么?还是有一种我不知道的扭曲?
编辑:我已经更新了我的代码以使用秒表,然而,相同的行为仍然存在.为了打折JIT的效果,我实际上尝试了几次点击两者button1并且button2没有特别的顺序.虽然我得到的时间可能不同,但定性行为仍然存在:在这种情况下PLINQ确实较慢.
我有这样的代码:
var list = new List<int> {1, 2, 3, 4, 5};
var result = from x in list.AsParallel()
let a = LongRunningCalc1(x)
let b = LongRunningCalc2(x)
select new {a, b};
Run Code Online (Sandbox Code Playgroud)
让我们说LongRunningCalc每种方法需要1秒钟.上面的代码大约需要2秒才能运行,因为虽然5个元素的列表是并行操作的,但是从let语句中调用的两个方法是按顺序调用的.
但是,这些方法也可以安全地并行调用.他们显然需要合并回来,select但直到那时应该并行运行 - select应该等待它们.
有没有办法实现这个目标?
假设我有一个大清单
List<long> longList = new List<long>(10000000)
Run Code Online (Sandbox Code Playgroud)
我想做以下查询:
bool found = longList.Contains(4345235234524245124L);
Run Code Online (Sandbox Code Playgroud)
有没有办法使用PLinq让每个线程只搜索列表的一小部分?
我知道在这种情况下使用Dictionary或HashMap会更好.这只是我想知道的关于PLinq的东西,这个例子非常方便.
关于PLINQ中的订单保存的msdn文档说明了以下内容ForAll().
这是否意味着ForAll永远不能保证该方法的有序执行?
我以前没有使用PLINQ,但以下Code Review问题似乎是适合它的用法.在我的回答的底部,我写道:
Events.AsParallel().AsOrdered().ForAll( eventItem =>
{
...
} );
Run Code Online (Sandbox Code Playgroud)
阅读文档后,我相信AsOrdered()不会改变任何东西?
我也怀疑以前的查询不能取代for顺序很重要的简单循环?
可能StringBuilder会发生与意志的并行调用,导致输出错误?
以下是跟踪进度的最佳方法
long total = Products.LongCount();
long current = 0;
double Progress = 0.0;
Parallel.ForEach(Products, product =>
{
try
{
var price = GetPrice(SystemAccount, product);
SavePrice(product,price);
}
finally
{
Interlocked.Decrement(ref this.current);
}});
Run Code Online (Sandbox Code Playgroud)
我想将进度变量从0.0更新为1.0(当前/总)但我不想使用会对并行性产生负面影响的任何内容.
在以下代码中,如果取消注释“using static”行,查询将不会并行运行。为什么?
(Visual Studio 社区 2019,.Net Core 3.1 / .Net 4.8)
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace UsingStatic_Mystery
{
//using static Enumerable;
class Program
{
static void Main(string[] args)
{
var w = new Stopwatch();
iter:
w.Start();
var xx = Enumerable.Range(0, 10)
.AsParallel()
.OrderByDescending(x => {
Thread.Sleep(new Random().Next(100));
Console.WriteLine(x);
return x;
}).ToArray();
w.Stop();
Console.WriteLine();
foreach (var x in xx) Console.WriteLine(x);
Console.WriteLine(w.ElapsedMilliseconds);
Console.ReadLine();
w.Reset();
goto iter;
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出,未注释/注释:
最近我对Linq和Plinq采取了一些措施.我无法看出Plinq在哪种情况下具有真正的显着优势.
我发现了许多例子,例如:
Enumerable.Range(0, 10000).AsParallel().ForAll(_ => Thread.Sleep(50000));
Run Code Online (Sandbox Code Playgroud)
这是完全没用的例子,因为它可以平均10000度,然后会更快10000倍,但在业务应用程序中很少循环10000次并制作"IO作业".
如果有人可以提一个例子,请发布.
Plinq仅适用于Linq to Objects和Linq to XML.不建议在webserver上运行的应用程序中使用它(它有自己的线程管理).因此,我们的机会减少到具有多个核心的桌面应用程序.
通常我会编写linq查询,这些查询在几分之一秒内按顺序运行.如果我平行化并且它真的会并行运行那么会更快,但是谁在乎呢?我认为它仍然足够快.最终用户不会看到任何差异.
我可以想象一个例子,当有人从数据库获取所有数据并在内存中运行查询时.也许这很有用,但这是一个错误的设计.
TPL是异步程序编程的好东西,但我仍然不确定Plinq是一个有用的工具.
有没有人有这方面的积极经验?