是否有更有效的方法来省略循环实例

时间:2012-03-30 18:36:02

标签: c# performance

如果我有一个标准for循环是否有更有效的方法来省略某些出现?

例如:

A:

        for (int i = 0; i < n; i++)
        {
            if (i != n / 2 && i != n / 3 && i != n / 4)
            {
                val += DoWork(i);
            }
            else
            {
                continue;
            }
        }

B:

        for (int i = 0; i < n / 4; i++)
        {
            val += DoWork(i); ;
        }
        for (int i = n / 4 + 1; i < n / 3; i++)
        {
            val += DoWork(i);
        }
        for (int i = n / 3 + 1; i < n / 2; i++)
        {
            val += DoWork(i);
        }
        for (int i = n / 2 + 1; i < n; i++)
        {
            val += DoWork(i);
        }

C:

        for (int i = 0; i < n; i++)
        {
            if (i == n / 2 || i == n / 3 || i == n / 4)
            {
                continue;
            }
            else
            {
                val += DoWork(i);
            }
        }

对于n = int.MaxValue,结果如下: 结果:57498毫秒。 B结果:42204毫秒。 C结果:57643毫秒。

根据@Churk的回答进行编辑。 我添加了另一个测试用例方法D,如下所示:

d

        for (int i = 0; i < n; i =  (i != n / 2 -1 && i != n / 3 -1  && i != n / 4-1) ? i+1: i+2)
        {
            val += DoWork(i);
        }

并得到以下结果:

结果:56355毫秒。 B结果:40612毫秒。 C结果:56214毫秒。 D结果:51810毫秒。

Edit2:预先计算出n / 2 n / 3和n / 4之外的值得到了:

结果:50873毫秒。 B结果:39514毫秒。 C结果:51167毫秒。 D结果:42808毫秒。

D循环似乎再次接近B的“展开”,A和C仍然花费更多的时间。

就每种方法之间的比较而言,这与我的预期有关。

我的问题是,有更有效的方法吗?

7 个答案:

答案 0 :(得分:3)

这取决于上下文。在许多情况下,一种可能是作弊。因此,不要省略数字,只需包含它们,然后将结果从您不想要的数字反转:

    for (int i = 0; i < n; i++) 
    { 
        val += DoWork(i); 
    } 
    val -= DoWork(i/2);
    val -= DoWork(i/3);
    val -= DoWork(i/4);

从比较中节省的时间可能会超过计算两次数字的结果,具体取决于DoWork操作的成本。

答案 1 :(得分:2)

将您的次要条件嵌入循环

未测试:

for (int i = 0; i < n || (i != n / 2 && i != n / 3 && i != n / 4); i++)
  val += DoWork(i);
}

我认为只要其中一个条件成立,它就能继续运行

答案 2 :(得分:1)

首先,在40-60秒内暂停几次,这样你就可以清楚地知道DoWork中有多少时间。如果你甚至可以让你的循环没有时间,你仍然需要花费那部分。

现在看看你的比较。 每个人都在问一个问题。 如果问题的答案几乎总是真或几乎总是假,那么这是一个获得加速的机会。 (所有log(n)和n * log(n)算法的工作原理是使他们的决策点更像是公平的硬币。)

所以你可以看到为什么你的B更快。平均而言,每个循环要求的问题更少。 通过展开循环(更频繁地询问问题),你可以更快地完成它。

(我知道,我知道,编译器可以展开循环。好吧,也许。自己做吧,你不必赌它。)

答案 3 :(得分:0)

您可以计算循环外的值,然后跳过它们:

  int skip1 = n/2;
  int skip2 = n/3;
  int skip3 = n/4;
  for (int i = 0; i < n; i++)
  {
      if (i != skip1 && i != skip2 && i != skip3)
      {
         val += DoWork(i);
      }
  }

不确定这是否会节省足够的毫秒时间。

更新:我刚刚注意到@surfen在评论中提出了这个建议。

答案 4 :(得分:-1)

如果DoWork()不是瓶颈,那么这是一个足够小的方法可以嵌入到循环中,因此您不需要自行花费时间的调用。如果DoWork()完成了大部分工作,那么你就是浪费时间的人:)

答案 5 :(得分:-1)

你可以计算操作......

选项A:
n检查for循环中的i,然后检查每个不是该值的i ...所以只有4n个操作用于检查。

选项B:
您只需循环显示间隔,因此您正在进行n-3操作。

选项C:
与选项A,4n操作相同。

答案 6 :(得分:-1)

我不会因为这些修改而使代码复杂化(因为很多人已经对你的问题发表了评论)

相反,您可能希望使用Pararell.ForEach在多个线程中同时运行DoWork。如果您的DoWork()做任何事情,这将对性能产生更大的影响。