如何优化大尺寸的循环

时间:2012-11-08 06:24:44

标签: c# performance .net-3.5

我有一个超过20k迭代的for循环,每次迭代需要大约两到三秒,总共大约20分钟。我如何优化这个循环。我正在使用.net3.5所以并行foreach是不可能的。所以我将200000 nos分成小块并实现了一些线程,现在我可以将时间缩短50%。有没有其他方法来优化这种for循环。

我的示例代码如下所示

    static double sum=0.0;
    public double AsyncTest()
    {
            List<Item> ItemsList = GetItem();//around 20k items
            int count = 0;
            bool flag = true;
            var newItemsList = ItemsList.Take(62).ToList();
            while (flag)
            {
                int j=0;
                WaitHandle[] waitHandles = new WaitHandle[62];
                foreach (Item item in newItemsList)
                {
                    var delegateInstance = new MyDelegate(MyMethod);
                    IAsyncResult asyncResult = delegateInstance.BeginInvoke(item.id, new AsyncCallback(MyAsyncResults), null);
                    waitHandles[j] = asyncResult.AsyncWaitHandle;
                    j++;
                }
                WaitHandle.WaitAll(waitHandles);
                count = count + 62;
                newItemsList = ItemsList.Skip(count).Take(62).ToList();  
            }
            return sum;
    }

    public double MyMethod(int id)
    {
        //Calculations
        return sum;
    }

    static public void MyAsyncResults(IAsyncResult iResult)
    {
        AsyncResult asyncResult = (AsyncResult) iResult;
        MyDelegate del = (MyDelegate) asyncResult.AsyncDelegate;
        double mySum = del.EndInvoke(iResult);
        sum = sum + mySum;
    }

6 个答案:

答案 0 :(得分:3)

可以通过各种技术减少循环次数。但是,由于在循环内部执行繁重的计算,因此不会给您任何明显的改进。如果您已经将其并行化以使用所有CPU内核,则没有太多工作要做。有一定量的计算要做,并且有一定的计算机功率可用。你无法从机器上挤出的东西超过它所能提供的。

您可以尝试:

  1. 如果可能的话,可以更有效地实施算法
  2. 切换到更快的环境/语言,例如非托管C / C ++。

答案 1 :(得分:1)

  1. 批次大小(62)背后是否存在理由?
  2. “MyMethod”方法是IO绑定还是CPU绑定?
  3. 您在每个周期中所做的是等到所有批次完成并且这浪费了一些周期(您实际上等待所有62个调用完成,然后再进行下一批次)。 为什么你不会稍微改变一下这个方法,以便你仍然可以同时运行N个操作,但只要一个一个的executind操作完成就会激活一个新的操作?

答案 2 :(得分:0)

根据这个blog,对于集合,for循环比foreach更快。尝试使用for进行循环。它会有所帮助。

答案 3 :(得分:0)

如果我错了,请纠正我,但在我看来,你的线程是在单个项目级别 - 我想知道这是否有点过于细化。

你已经在62件物品中完成了你的工作。如果您要在一个线程中获取这些项目并处理所有项目,该怎么办?即,你会有这样的事情:

void RunMyMethods(IEnumerable<Item> items)
{
    foreach(Item item in items)
    {
        var result = MyMethod(item);
        ...
    }
}

请注意,WaitHandle个对象可能比使用Monitor个对象要慢:http://www.yoda.arachsys.com/csharp/threads/waithandles.shtml

否则,通常的建议是:对性能进行分析以找出真正的瓶颈。在你的问题中,你说每次迭代需要2-3秒 - 有20000次迭代,需要花费20多分钟。

修改

如果您希望最大限度地利用CPU时间,那么最好将20000个项目拆分为四个5000组,并在其自己的线程中处理每个组。我可以想象,这种“厚实的”粗糙的并发性比非常细粒度的方法更有效。

答案 4 :(得分:0)

听起来你有一个CPU密集型MyMethod。对于CPU密集型任务,您可以通过并行化获得显着改进,但仅限于更好地利用所有CPU核心。除此之外,过多的并行化可能会开始伤害性能 - 我认为这就是你正在做的事情。 (这与I / O密集型任务不同,您可以尽可能多地并行化。)

在我看来,你需要做的是写另一种方法来获取一大块物品(不是单个物品)并返回它们的“总和”:

double SumChunk(IEnumerable<Item> items)
{
    return items.Sum(x => MyMethod(x));
}

然后将项目数除以 n n 是并行度 - 尝试n = CPU核心数,并将其与x2进行比较)并通过每个块到SumChunk的异步任务。最后,总结一下子结果。

另外,观察是否有任何块在其他块之前完成。如果是这种情况,那么您的任务分配不是均质的。您需要创建较小的块(比如说300个块的块)并将它们传递给SumChunk

答案 5 :(得分:0)

首先,这些数字不会添加:

  

20k次迭代,每次迭代需要大约两到三秒,总共大约20分钟

这是一个 x40 '并行因素' - 你永远无法在普通机器上运行。

其次,当'优化'CPU密集型计算时,并行化超出核心数量是没有意义的。尝试将那些神奇的 62 放到 16 并进行基准测试 - 它实际上会更快地运行

我在笔记本电脑上运行了一段畸形的恶意代码,并使用Parallel.ForEach

提高了10-20%

所以也许你可以让它运行17分钟而不是20分钟 - 真的重要吗?