这是C#中的一段代码,它对双精度矩阵的每一行应用一个操作(假设为200x200)。
For (int i = 0; i < 200; i++)
{
result = process(row[i]);
DoSomething(result);
}
流程 是一个静态方法,我有一个 Corei5 CPU和 Windows XP 我正在使用 .Net Framework 3.5 。为了获得性能,我尝试使用单独的线程处理每一行(使用 Asynchronous delegates )。所以我重写了如下代码:
List<Func<double[], double>> myMethodList = new List<Func<double[], double>>();
List<IAsyncResult> myCookieList = new List<IAsyncResult>();
for (int i = 0; i < 200; i++)
{
Func<double[], double> myMethod = process;
IAsyncResult myCookie = myMethod.BeginInvoke(row[i], null, null);
myMethodList.Add(myMethod);
myCookieList.Add(myCookie);
}
for (int j = 0; j < 200; j++)
{
result = myMethodList[j].EndInvoke(myCookieList[j]);
DoSomething(result);
}
此代码在一次运行中被调用1000个矩阵。当我测试时,令人惊讶的是我没有得到任何性能提升!所以这给我提出了这个问题,即在多数情况下多线程对提高性能有好处,而且我的代码也符合逻辑?
答案 0 :(得分:2)
通过使用BeginInvoke
调用AsyncCallback
,您可以实现更多并行性(在结果处理中) - 这将在ThreadPool
线程中执行结果处理,而不是内联你现在有。
请参阅异步编程文档的最后一部分here。
在您做任何修改代码的事情之前,您应该对其进行分析,以找出该程序花费时间的位置。
答案 1 :(得分:2)
乍一看,您的代码看起来不错。也许CPU不是瓶颈。
您能否确认process()
和DoSomething()
是独立的,并且不对共享资源执行任何I / O或锁定?
这里的要点是你必须开始测量。
当然,使用TPL的Fx4使这种事情更容易编写,并且通常更有效率。
答案 2 :(得分:1)
你的代码有点过分了。看看循环;对于200次迭代中的每次迭代,您都要创建一个新线程来进行异步调用。这将导致您的进程具有201个活动线程。有一个递减收益的法则;大约是线程数量的两倍,因为处理器具有“执行单元”的数量(CPU数量,每个CPU上核心数量的倍数,X2,如果核心可以是超线程的),您的计算机将开始消费调度线程的时间多于运行它们的时间。最先进的服务器有4个四核HT CPU,大约32个EU。 200个主动执行的线程将使该服务器崩溃并哭泣。
如果处理顺序无关紧要,我会实现类似MergeSort的算法;将数组分成两半,处理左手,处理右手。每个“左手”都可以由新线程处理,但是处理当前线程中的“右手”。然后,实现一些线程安全的手段,将线程数限制为“执行单元”数量的1.25倍;如果已达到限制,则继续线性处理,而不创建新线程。
答案 3 :(得分:0)
由于您处理EndInvoke方法调用的方式,看起来您没有获得任何性能。由于您使用BeginInvoke调用“process”,因此这些函数调用会立即返回,因此第一个循环可能很快就会完成。但是,EndInvoke会阻塞,直到调用它的调用完成处理,您仍然按顺序使用它。正如史蒂夫所说,你应该使用AsyncCallback,以便每个完成事件都在它自己的线程上处理。
答案 4 :(得分:0)
你没有看到太大的收获,因为你没有并行化代码,是的,你正在做异步,但这只是意味着你的循环不等待计算才能进入下一步。使用Parallel.For而不是for循环,看看你的多核盒子上是否有任何增益...
答案 5 :(得分:0)
如果您打算使用异步委托,这将是确保在线程池线程上发生回调的方法;
internal static void Do()
{
AsyncCallback cb = Complete;
List<double[]> row = CreateList();
for (int i = 0; i < 200; i++)
{
Func<double[], double> myMethod = Process;
myMethod.BeginInvoke(row[i], cb, null);
}
}
static double Process (double[] vals)
{
// your implementation
return randy.NextDouble();
}
static void Complete(IAsyncResult token)
{
Func<double[], double> callBack = (Func<double[], double>)((AsyncResult)token).AsyncDelegate;
double res = callBack.EndInvoke(token);
Console.WriteLine("complete res {0}", res);
DoSomething(res);
}