【问题标题】:Fast Enumeration slower than for-loop in nested enumeration (with test-results)?快速枚举比嵌套枚举中的for循环慢(带有测试结果)?
【发布时间】:2012-07-11 13:42:38
【问题描述】:

我知道有相当多的主题似乎是关于完全相同的事情,但我没有找到一个真正关于我想要的东西。

所以我很好奇,想将快速枚举的性能与 NSEnumerator 和 for 循环进行比较。 (这是被问得比较频繁的部分)

首先我比较了快速枚举:

for(NSNumber *number in testArray)
{
    assert(number);
}

NSE 分子:

NSEnumerator *enumerator = [testArray objectEnumerator];
NSNumber *number;
while (number = [enumerator nextObject]) 
{
    assert(number);
}

for循环:

for(NSUInteger i = 0; i < [testArray count]; i++)
{
    NSNumber *number = [testArray objectAtIndex:i];
    assert(number);
}

我的testArray 是一个由 0 到 1,000,000 的 NSNumber 组成的数组,我一个接一个地运行了 100 次测试,并计算了每个测试的平均运行时间。

我也在我的 iPad 2 上运行它们

结果:(所有 100 次运行的平均时间)

  • 0.042687s 快速枚举
  • 0.582072s NSEnumerator
  • 0.627318s for循环

正如预期的那样,Fast Enumeration 是迄今为止最快的,而 NSEnumerator 仍然比 for 循环快一点,但这是为了枚举退出一个大数组

所以这是一个不太常见的问题:

其实我对别的东西很感兴趣:在数组中枚举以比较数组中的每个对象

第一次尝试嵌套for循环:

for(int i = 0; i < [testArray count]-1; i++)
{
    NSNumber *number = [testArray objectAtIndex:i];
    for(int j = i+1; j < [testArray count]; j++)
    {
        NSNumber *innerLoopNumber = [testArray objectAtIndex:j];
        assert(innerLoopNumber);
        assert(number);
    }
}

对于这些测试,我必须减小数组的大小和运行次数才能在合理的时间内完成它们,因为迭代次数当然会随着 O(n^2) 的增长而增长。 所以我用一个包含 5.000 个 NSNumber 的数组运行它们并重复测试 5 次。

结果:7.360645s 1 次运行

所以我想,当然,快速枚举应该更快。但是为了实现三角形模式以避免对每对元素进行两次比较,我不得不将外循环中的 Fast Enumeration 与内循环中的 NSEnumerator 混合使用

for(NSNumber *number in testArray)
{
    NSEnumerator *reverseEnumterator = [testArray reverseObjectEnumerator];
    NSNumber *innerLoopNumber = reverseEnumterator.nextObject;
    while(innerLoopNumber && ![innerLoopNumber isEqualToNumber:number])
    {
        innerLoopNumber = reverseEnumterator.nextObject;
        assert(innerLoopNumber);
        assert(number);
    }
}

令我惊讶的是,这要慢得多:18.086980s 1 次运行

然后我也尝试了一个混合版本,外部循环使用快速枚举,内部循环使用 for 循环:

int counter = 0;
for(NSNumber *number in testArray)
{
    for(int j = counter +1; j < [testArray count]; j++)
    {
        NSNumber *innerLoopNumber = [testArray objectAtIndex:j];
        assert(innerLoopNumber);
        assert(number);
    }
    counter++;
}

结果:7.079600s 1 次运行

比普通的 for 循环稍微快一点。

一个地方的数字:

  • 07.360645s for-循环
  • 07.079600s混合
  • 18.086980s 快速枚举

所以我想知道,这是为什么呢?快速枚举是否仅在“未中断”时才能正常工作,使用 NSEnumerator 是否会干扰快速枚举? 还是我只是遗漏了什么而我的方法有误?

【问题讨论】:

  • 请注意,在 for 循环的每次迭代中,您都在重复调用方法 [testArray count]
  • 根据苹果的文档,当使用for-loop:"时,在每次循环迭代中,都会派发消息来获取数组中的项数,这样很浪费。如果数组中的项数数组永远不会改变,你可以将该值分配给一个变量并使用它来代替"

标签: objective-c arrays performance for-loop fast-enumeration


【解决方案1】:

您正在快速枚举循环中调用其他方法。 Objective-c 具有重要的方法调用开销,因此您的问题在于嵌套循环的设置。如您所见,快速枚举 + for 循环比 for 循环 + for 循环要快,而且这里避免了额外的方法调用。

【讨论】:

  • 嗯,我明白你的意思了。等式检查在其他构造中不是必需的。但是检查是 n^2 次迭代和 (n^2)/2 次迭代之间的区别。所以这将取决于循环中实际操作的成本,但在我看来,对于这种情况,嵌套的快速枚举构造根本不适合。
  • 试试不做相等性检查,我敢打赌,与其他类型做一半检查相比,快速枚举在没有额外调用开销的情况下进行两倍的检查会更快。
  • 我试过了:15.004834s,比比较快一点,但仍然几乎是其他解决方案的两倍。如果我将真正的工作放在内部循环中,而不仅仅是断言,那么它通过内部循环两次的事实只会变得更加昂贵。不过我发现了一些更有趣的东西:在外循环中创建一个 subarrayWithRange 然后通过它快速枚举可以达到同样的效果,但速度比目前为止的 2.626523s 快得多,是 for 循环的两倍多,而且真的双快速枚举
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2017-09-01
  • 2019-06-26
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多