C# 测量并行的执行时间
我正在使用Parallel.For循环来提高计算的执行速度 我想计算一下剩下的大概计算时间。通常情况下,只需测量每一步所需的时间,并通过将步时间乘以总步数来估计总时间 e、 例如,如果有100个步骤,有些步骤需要5秒,那么除了总时间约为500秒外,其他步骤都可以完成。(一个人可以在几个步骤中求平均值,并不断地向用户报告这正是我想要做的) 我能想到的唯一方法是使用一个外部for循环,它本质上是回到原来的方式,通过拆分并行for间隔并测量每个间隔C# 测量并行的执行时间,c#,.net,C#,.net,我正在使用Parallel.For循环来提高计算的执行速度 我想计算一下剩下的大概计算时间。通常情况下,只需测量每一步所需的时间,并通过将步时间乘以总步数来估计总时间 e、 例如,如果有100个步骤,有些步骤需要5秒,那么除了总时间约为500秒外,其他步骤都可以完成。(一个人可以在几个步骤中求平均值,并不断地向用户报告这正是我想要做的) 我能想到的唯一方法是使用一个外部for循环,它本质上是回到原来的方式,通过拆分并行for间隔并测量每个间隔 for(i;n;i += step) Tim
for(i;n;i += step)
Time(Parallel.For(i, i + step - 1, ...))
一般来说,这不是一个很好的方法,因为一些很长的步骤或大量的短步骤都会导致计时问题
有人有什么想法吗
(请注意,我需要实时估计并行计算所需的时间,而不是总时间。我想让用户知道还剩下多少执行时间)。我建议在完成时在每个步骤报告执行方法。当然,这对于线程安全来说有点棘手,所以在实现时要记住这一点。这将使您能够跟踪总任务中已完成任务的数量,并且(某种程度上)很容易知道每个步骤花费的时间,这对于删除异常值等非常有用 编辑:一些代码来演示这个想法
Parallel.For(startIdx, endIdx, idx => {
var sw = Stopwatch.StartNew();
DoCalculation(idx);
sw.Stop();
var dur = sw.Elapsed;
ReportFinished(idx, dur);
});
这里的关键是,
ReportFinished
将为您提供有关已完成任务数量以及每个任务持续时间的连续信息。通过对这些数据进行统计,您可以更好地猜测剩余时间。这几乎是不可能回答的
首先,不清楚所有的步骤都做了什么。某些步骤可能是I/O密集型或计算密集型的
此外,Parallel.For是一个请求——您不确定您的代码是否会实际并行运行。这取决于环境(线程和内存的可用性)是否实际并行运行代码。然后,如果您有依赖于I/O的并行代码,则一个线程将在等待I/O完成时阻塞其他线程。你也不知道其他进程在做什么
这就是为什么预测某件事需要多长时间极易出错,实际上是徒劳的。这个问题很难回答。您提到的使用非常长的步骤或大量非常短的步骤的计时问题可能与您的循环将在并行分区器可以处理的边缘工作有关 由于默认的分区器是非常动态的,而且我们对您的实际问题一无所知,因此没有一个好的答案可以让您在解决手头的问题的同时,仍然可以通过动态负载平衡获得并行执行的好处
如果对预计的运行时间进行可靠的估计非常重要,那么您可以设置一个分区,然后利用您对分区的了解,从一个线程上的几个块中推断时间。这里有一个可能的解决方案来测量所有以前完成的任务的平均值。在每个任务完成后,将调用一个
操作
,您可以在其中汇总所有时间并将其除以完成的总任务。然而,这只是当前状态,无法预测未来的任务/平均值。(正如其他人提到的,这相当困难)
但是:您必须测量它是否适合您的问题,因为在方法级别声明的变量上都可能存在锁争用
static void ComputeParallelForWithTLS()
{
var collection = new List<int>() { 1000, 2000, 3000, 4000 }; // values used as sleep parameter
var sync = new object();
TimeSpan averageTime = new TimeSpan();
int amountOfItemsDone = 0; // referenced by the TPL, increment it with lock / interlocked.increment
Parallel.For(0, collection.Count,
() => new TimeSpan(),
(i, loopState, tlData) =>
{
var sw = Stopwatch.StartNew();
DoWork(collection, i);
sw.Stop();
return sw.Elapsed;
},
threadLocalData => // Called each time a task finishes
{
lock (sync)
{
averageTime += threadLocalData; // add time used for this task to the total.
}
Interlocked.Increment(ref amountOfItemsDone); // increment the tasks done
Console.WriteLine(averageTime.TotalMilliseconds / amountOfItemsDone + ms.");
/*print out the average for all done tasks so far. For an estimation,
multiply with the remaining items.*/
});
}
static void DoWork(List<int> items, int current)
{
System.Threading.Thread.Sleep(items[current]);
}
static void ComputeParallelForWithTLS()
{
var collection=new List(){1000,2000,3000,4000};//用作睡眠参数的值
var sync=新对象();
TimeSpan averageTime=新的TimeSpan();
int-amountOfItemsDone=0;//由TPL引用,使用lock/interlocked.increment递增
Parallel.For(0,collection.Count,
()=>新的时间跨度(),
(i,loopState,tlData)=>
{
var sw=Stopwatch.StartNew();
嫁妆(收藏,一);
sw.Stop();
返回经过的开关;
},
threadLocalData=>//每次任务完成时调用
{
锁定(同步)
{
averageTime+=threadLocalData;//将用于此任务的时间添加到总时间中。
}
Increment(ref amountOfItemsDone);//增加已完成的任务
Console.WriteLine(averageTime.TotalMillicles/AmountFitemsOne+ms.”;
/*打印到目前为止所有已完成任务的平均值。对于估算,
与剩余项目相乘*/
});
}
静态无效工作(列表项,当前整数)
{
System.Threading.Thread.Sleep(项[当前]);
}
这种方法似乎非常有效。我们可以通过让每个并行循环增加一个计数器来“线性化”并行for循环:
Parallel.For(0, n, (i) => { Thread.Sleep(1000); Interlocked.Increment(ref cnt); });
(注意,多亏了Niclas,++
不是原子的,必须使用锁
或互锁。增量
)
每个并行运行的循环都将增加cnt
。其效果是cnt
单调增加到n
,而cnt/n
是for完成的百分比。由于对cnt
没有争用,因此不存在并发问题,这非常复杂
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ParallelForTiming
{
class Program
{
static void Main(string[] args)
{
var sw = new Stopwatch();
var pct = 0.000001;
var iter = 20;
var time = 20 * 1000 / iter;
var p = new ParallelOptions(); p.MaxDegreeOfParallelism = 4;
var Done = false;
Parallel.Invoke(() =>
{
sw.Start();
Parallel.For(0, iter, p, (i) => { Thread.Sleep(time); lock(p) { pct += 1 / (double)iter; }});
sw.Stop();
Done = true;
}, () =>
{
while (!Done)
{
Console.WriteLine(Math.Round(pct*100,2) + " : " + ((pct < 0.1) ? "oo" : (sw.ElapsedMilliseconds / pct /1000.0).ToString()));
Thread.Sleep(2000);
}
}
);
Console.WriteLine(Math.Round(pct * 100, 2) + " : " + sw.ElapsedMilliseconds / pct / 1000.0);
Console.ReadKey();
}
}
}
public static class Counter
{
private static long _seriesProcessedItems = 0;
private static long _totalProcessedItems = 0;
private static TimeSpan _totalTime = TimeSpan.Zero;
private static DateTime _operationStartTime;
private static object _lock = new object();
private static int _numberOfCurrentOperations = 0;
public static void StartAsyncOperation()
{
lock (_lock)
{
if (_numberOfCurrentOperations == 0)
{
_operationStartTime = DateTime.Now;
}
_numberOfCurrentOperations++;
}
}
public static void EndAsyncOperation(int itemsProcessed)
{
lock (_lock)
{
_numberOfCurrentOperations--;
if (_numberOfCurrentOperations < 0)
throw new InvalidOperationException("EndAsyncOperation without StartAsyncOperation");
_seriesProcessedItems +=itemsProcessed;
if (_numberOfCurrentOperations == 0)
{
_totalProcessedItems += _seriesProcessedItems;
_totalTime += DateTime.Now - _operationStartTime;
_seriesProcessedItems = 0;
}
}
}
public static double GetAvgSpeed()
{
if (_totalProcessedItems == 0) throw new InvalidOperationException("_totalProcessedItems is zero");
if (_totalProcessedItems == 0) throw new InvalidOperationException("_totalTime is zero");
return _totalProcessedItems / (double)_totalTime.TotalMilliseconds;
}
public static void Reset()
{
_totalProcessedItems = 0;
_totalTime = TimeSpan.Zero;
}
}
static void Main(string[] args)
{
var st = Stopwatch.StartNew();
Parallel.For(0, 100, _ =>
{
Counter.StartAsyncOperation();
Thread.Sleep(100);
Counter.EndAsyncOperation(1);
});
st.Stop();
Console.WriteLine("Speed correct {0}", 100 / (double)st.ElapsedMilliseconds);
Console.WriteLine("Speed to test {0}", Counter.GetAvgSpeed());
}