Java 应使用多个回路或一个回路并检查内部条件

Java 应使用多个回路或一个回路并检查内部条件,java,c++,c,performance,code-cleanup,Java,C++,C,Performance,Code Cleanup,我有两个任务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 } for(int i=0;i

我有两个任务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
}
for(int i=0;i<10;++i)
{
//做任务
}
对于(int i=0;i<20;++i)
{
//做任务B
}
解决方案2:

for (int i = 0; i < 20; ++i)
{
    if (i < 10)
    { 
        //do taskA
    }
   // do taskB
}
for(int i=0;i<20;++i)
{
如果(i<10)
{ 
//做任务
}
//做任务B
}

我想问解决方案1或解决方案2在性能和干净代码方面是否更好?

根据您的示例,解决方案2会更好,因为您只循环一次。但您必须考虑任务A和任务B中的逻辑。任务A和任务B之间是否存在性能差异。但是在IMO中,使用带有条件(
i>10
)的单循环将使代码的可读性降低

因此,您可以以更简洁的方式生成等效代码:

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

for (int i = 10; i < 20; ++i)
{
  // do taskB
}
for(int i=0;i<10;++i)
{
//做任务
//做任务B
}
对于(int i=10;i<20;++i)
{
//做任务B
}

这相当于解决方案2,更好的程度取决于任务A和B。

如其他人所述,很大程度上取决于在这些循环中执行的任务类型。尽管默认选项应为选项1,以确保可读性。 但是,有一种情况是,选项1应该是出于语法上的原因而不是出于语法上的原因而做出的选择。如果任务A和任务B在各自的内存块上工作,那么它们的性能很可能会优于选项2

例如:

for(int i = 0 ; i < 10; i++){
     doSomething = dataBlockForTaskA[i]; // lots of cache hits
}
for(int i=0;i<10;i++){
doSomething=dataBlockForTaskA[i];//大量缓存命中
}
vs

for(int j=0;j<20;j++){
doSomething=dataBlockForTaskB[j];//获取块B周围的一些内存
如果(j<10){
doSOmething=dataBlockForTaskA[j]//oops缓存未命中
}
} 

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

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

因此,除了两点之外,没有多少客观的答案了:

  • 共同最佳做法
  • 演出

关于最佳实践,我想提及。如果任务是完全独立的(它们显然是独立的,否则您甚至没有机会更改执行顺序),那么关注点分离原则建议将执行分为两个独立的循环。因此,这有利于您的第一个解决方案。人们甚至可以考虑进一步走一步,把它们分成两种方法:

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
    }
}
(在这里可以更进一步,但可能没有抓住问题的重点)


关于性能,通常不会有太大的差异,尽管细节可能取决于任务和编译器。现代编译器优化得非常好,甚至可能展开循环,因此第一个解决方案相当于

taskA();
... // 10 times
taskA();
taskB();
... // 20 times
taskB();
taskB();
taskA();
... // 10 times
taskB();
taskA();
taskB();
taskB();
... // 10 times
taskB();
taskB();
第二种解决方案相当于

taskA();
... // 10 times
taskA();
taskB();
... // 20 times
taskB();
taskB();
taskA();
... // 10 times
taskB();
taskA();
taskB();
taskB();
... // 10 times
taskB();
taskB();
即使循环未展开,也会处理
if
。然而,由于以下几个原因,首先解决方案仍然更可取:

  • (数据)如所指出的位置
  • 可能的本质
  • 使循环并行化的可能性
后者指的是这样一个事实,即您可以简单地并行化一个简单的循环,如

for (int i = 0; i < times; ++i) {
    taskA();
}
for(int i=0;i
例如,在java(或C++中,假设你有一个适当的基础结构),你可以简单地把10个实例的代码< TaskA<代码>扔进一个线程池中。用类似于
#pragma omp parallel
的东西注释这样的循环也是很容易的(尽管我不熟悉OpenMP)。如果存在
If
-条件,这将干扰大多数并行化方法


因此,根据这些观察结果,我投票支持解决方案1,为什么不运行一个基准测试,看看呢?现在,这似乎相当主观,因此可能不适合,但从我的角度来看,解决方案1更清晰,因为您清楚地划分了两个过程,这样,如果特定任务的迭代次数发生变化,您就不必麻烦更改测试。这就是说,有三个备选方案:1)从0迭代到9,并执行两个任务2)从10到19的第二次迭代仅执行任务B只有当您必须交替执行10个步骤的任务时,第二个才是好的。否则,它不会表示代码应该做什么,所以您应该先编写。将一个整数增加十倍不太可能对性能产生丝毫影响。我想要求性能和干净的代码。这是一个小例子,但在一个大项目中,我认为这是一个大问题。@trunnguyen a)什么语言?可能是你正在使用C、C++和java来做一个项目,但我有疑问。B) 你为什么认为这是一个“大问题”?@ceekay是的,谢谢。谢谢你的回答。你的解决方案很好。但是请告诉我解决方案1和解决方案2。解决方案1执行30次循环,解决方案2执行20次循环+20次检查条件,因此哪一个更好。仅通过比较循环计数,无法确定是否有任何差异。从技术上讲,解决方案2(以及我的答案)可能会更好,因为它们循环次数更少。但这本身并不足以提出任何性能要求。执行这两个命令并测量性能,看看是否有任何不同。