应该使用许多循环或一个循环并检查内部条件

时间:2015-05-20 09:09:59

标签: java c++ c performance code-cleanup

我有2个任务TaskA和TaskB,TaskA将被处理10次,taskB将被处理20次。现在有两个解决方案,如下所示 解决方案1:

for (int i = 0; i < 10; ++i)
{
   // do taskA
}
for (int i = 0; i < 20; ++i)
{
   // do taskB
}

解决方案2:

for (int i = 0; i < 20; ++i)
{
    if (i < 10)
    { 
        //do taskA
    }
   // do taskB
}

我想问一下解决方案1或解决方案2是否更适合性能和干净的代码?

4 个答案:

答案 0 :(得分:4)

是否会产生任何性能差异是taskA和taskB的作用。但IMO,具有条件(i>10)的单循环将使代码的可读性降低。

因此,您可以更清晰地制作等效代码:

for (int i = 0; i < 10; ++i)
{
  // do taskA
  // do taskB
}

for (int i = 10; i < 20; ++i)
{
  // do taskB
}

这相当于解决方案2,并且更好地取决于任务A和B.

答案 1 :(得分:2)

正如已经多次指出的那样,这完全取决于“任务”是什么。

人们可能会争论“风格”或“可读性”,但这显然是主观的。

除了两点之外,没有太多的目标答案:

  • 常见的最佳做法
  • 性能

关于最佳实践,我想参考Separation Of Concerns。如果任务完全独立(并且它们显然是 - 否则你甚至没有机会改变执行顺序),那么关注点分离的原则建议将执行放入两个单独的循环中。所以这有利于您的第一个解决方案。人们甚至可以考虑更进一步,并将其分为两种方法:

void executeA(int times) {
    for (int i = 0; i < times; ++i) {
        // do taskA
    }
}

void executeB(int times) {
    for (int i = 0; i < times; ++i) {
        // do taskB
    }
}

并用这些方法替换循环:

executeA(10);
executeB(20);

(这里可能会更进一步,但可能会忽略问题的重点)

关于性能,通常没有太大差异,尽管细节可能取决于任务和编译器。现代编译器正在优化,可能甚至展开循环,因此第一个解决方案将等同于

taskA();
... // 10 times
taskA();
taskB();
... // 20 times
taskB();

,第二个解决方案相当于

taskB();
taskA();
... // 10 times
taskB();
taskA();
taskB();
taskB();
... // 10 times
taskB();
taskB();

即使未展开循环,Branch Prediction也会处理if。但是,由于以下几个原因,第一解决方案仍然会更受欢迎:

后者指的是你可以简单地并行化一个简单的循环,如

for (int i = 0; i < times; ++i) {
    taskA();
}

例如,在Java(或C ++中,假设您有适当的基础结构),您可以简单地将10个taskA实例抛出到线程池中。同样用#pragma omp parallel之类的东西注释这样的循环很容易(虽然我不熟悉OpenMP)。如果存在if条件,则会干扰大多数并行化方法。

基于这些观察结果,我投票赞成解决方案1 ​​

答案 2 :(得分:1)

正如其他人所提到的,很大程度上取决于在这些循环中执行的任务类型。虽然默认选择应该是选项1以便于阅读。 有一种情况,其中选项1 应该选择的原因超过语法糖。如果任务A和任务B将在他们自己的内存块上工作locality of reference,那么最有可能胜过选项2。

例如:

for(int i = 0 ; i < 10; i++){
     doSomething = dataBlockForTaskA[i]; // lots of cache hits
}

VS

for (int j = 0; j < 20; j++){
    doSomething = dataBlockForTaskB[j]; // fetches some memory around BlockB
    if ( j < 10 ){
         doSOmething = dataBlockForTaskA[j] // oops cache miss
    }
} 

答案 3 :(得分:0)

根据您的示例解决方案2,基于您只循环一次的事实会更好。但是你必须考虑任务A与任务B中的逻辑。