Objective-C 空数组性能

发布于 2024-09-29 01:00:28 字数 1191 浏览 0 评论 0原文

我有一个应用程序,每一步都会迭代数组,当数组为空时,结果似乎出奇地慢。因此,我进行了一些后续测试,结果如下:

NSMutableArray* ar = [NSMutableArray array];
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < 10000; i++)
{
    for (NSObject* obj in ar)
    {
        [obj retain];
        [obj release];
    }
}
time = CFAbsoluteTimeGetCurrent() - time;   
printf("Empty Time: %1.12f", time / 10000.0f);

time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < 10000; i++)
{
    if ([ar count] > 0)
    {
        for (NSObject* obj in ar)
        {
            [obj retain];
            [obj release];
        }
    }
}
time = CFAbsoluteTimeGetCurrent() - time;   
printf("Checked Time: %1.12f", time / 10000.0f);

我尝试了 100 | 1,000 | 10,000 次迭代间隔,结果如下:

Empty Time: 0.000000039935          //100
Checked Time: 0.000000020266        //100
Empty Time: 0.000000018001          //1000
Checked Time: 0.000000011027        //1000
Empty Time: 0.000000015503          //10000
Checked Time: 0.000000008899        //10000

奇怪的是,这表明简单的计数检查可以显着提高低迭代运行的性能(可能是因为缓存方案)。这对我来说绝对是令人惊讶的,因为我期望 Objective-C 编译/运行时在执行 foreach 循环时已经执行此检查!有谁知道为什么会出现这种情况,以及是否有任何方法可以从该循环设置中挤出更多性能?谢谢!

I have an application that iterates over an array every step and I seem to be getting surprisingly slow results when the array is empty. So, I investigated with some follow-up tests that went something like this:

NSMutableArray* ar = [NSMutableArray array];
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < 10000; i++)
{
    for (NSObject* obj in ar)
    {
        [obj retain];
        [obj release];
    }
}
time = CFAbsoluteTimeGetCurrent() - time;   
printf("Empty Time: %1.12f", time / 10000.0f);

time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < 10000; i++)
{
    if ([ar count] > 0)
    {
        for (NSObject* obj in ar)
        {
            [obj retain];
            [obj release];
        }
    }
}
time = CFAbsoluteTimeGetCurrent() - time;   
printf("Checked Time: %1.12f", time / 10000.0f);

I tried this for 100 | 1,000 | 10,000 iteration intervals with the following results:

Empty Time: 0.000000039935          //100
Checked Time: 0.000000020266        //100
Empty Time: 0.000000018001          //1000
Checked Time: 0.000000011027        //1000
Empty Time: 0.000000015503          //10000
Checked Time: 0.000000008899        //10000

Strangely, this shows that having the simply count check significantly improves performance on low-iteration runs (probably because of caching schemes). This is absolutely astonishing for me as I expected the Objective-C compile/runtime to already do this check whenever a foreach loop is executed! Does anyone have any idea why this might be the case and if there is any way to squeeze even more performance out of this loop setup? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

∝单色的世界 2024-10-06 01:00:28

空数组在典型的 Cocoa 程序中并不常见,也不会迭代空数组数十数千次。

如果在 Instruments 中看到空数组的枚举作为 CPU 周期的重要消耗者,那将是非常令人惊讶的。

鉴于 Foundation 和 Core Foundation 针对现实世界的性能模式进行了优化,因此不进行 0 计数检查也就不足为奇了。

但是,如果您确实必须无数次迭代空数组,那么最快的方法是使用块:

time = CFAbsoluteTimeGetCurrent();
[ar enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [obj retain];
    [obj release];
}];

我将您的代码粘贴到 Foundation 工具的 main() 中,并在相对较新的 MacBook Pro 上得到了这个:

     Empty Time: 0.000000019896
   Checked Time: 0.000000007498
     Block Time: 0.000000000298

当然,而不是空数组,只需使用nil。即,我在 ar = nil; 后第二次进行了所有测试。

ar = nil;
time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < 10000; i++)
{
    for (NSObject* obj in ar)
    {
        [obj retain];
        [obj release];
    }
}
... etc ...


      Empty Time: 0.000000019902
    Checked Time: 0.000000007999
      Block Time: 0.000000000298
  nil Empty Time: 0.000000015599
nil Checked Time: 0.000000004703
  nil Block Time: 0.000000000000

不过,总的来说,如果您的数据结构非常复杂,并且您在每个帧渲染上都对它们进行了大量的操作,那么我建议可能需要使用不同的数据结构。

当然,前提是您确实使用 Instruments 来对代码进行采样,并且正在优化占用总体 CPU 周期很大一部分的内容。

Empty arrays aren't very common in a typical Cocoa program, nor would iterating an empty array 10s of thousands of times.

It would be exceptionally surprising to ever see enumeration of empty arrays show up in Instruments as a significant consumer of CPU cycles.

Given that the Foundation and Core Foundation are optimized to real world performance patterns, it isn't surprising that no 0 count check is made.

However, if you really must iterate an empty array a bazillion times, the fastest way is to use a block:

time = CFAbsoluteTimeGetCurrent();
[ar enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [obj retain];
    [obj release];
}];

I pasted your code into a Foundation tool's main() and got this on a relatively recent MacBook Pro:

     Empty Time: 0.000000019896
   Checked Time: 0.000000007498
     Block Time: 0.000000000298

Of course, instead of empty arrays, just use nil. I.e. I did all the tests a second time after ar = nil;.

ar = nil;
time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < 10000; i++)
{
    for (NSObject* obj in ar)
    {
        [obj retain];
        [obj release];
    }
}
... etc ...


      Empty Time: 0.000000019902
    Checked Time: 0.000000007999
      Block Time: 0.000000000298
  nil Empty Time: 0.000000015599
nil Checked Time: 0.000000004703
  nil Block Time: 0.000000000000

Overall, though, if your data structures are that complex and you are pounding on them that much on every frame render, I'd suggest a different data structure might be in order.

Of course, only if you've actually used Instruments to sample the code and are optimizing something that is consuming a significant percentage of overall CPU cycles.

呢古 2024-10-06 01:00:28

for-in 构造不是免费的,它必须解析某种枚举方法调用,因此报告的时间实际上是有意义的。在这种情况下我会使用纯 C 数组。另外,如果您使用 objc_msgsend() 在如此大的循环中调用 objc 方法,您将获得更好的性能。

The for-in construct is not free, it must be resolving to some kind of enumeration method call, so the times reported actually make sense. I would use plain C array in this case. Also you will get better performance if you use objc_msgsend() to call objc methods in such big loops.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文