使用@autoreleasepool降低峰值内存使用量

我在一个iPad应用程序上工作,这个应用程序有一个同步过程,在紧密的循环中使用Web服务和核心数据。 为了减less内存占用根据苹果的build议我分配和排出一个NSAutoreleasePool定期。 这目前工作很好,并没有目前的应用程序的内存问题。 但是,我打算转移到ARC, NSAutoreleasePool不再有效,并希望保持这种相同的性能。 我创build了一些例子并对它们进行了定时, 我想知道使用ARC来实现同样的性能并保持代码可读性的最佳方法是什么

出于testing目的,我想出了3个场景,每个使用1到10,000,000之间的数字创build一个string。 我运行了每个示例3次,以确定他们使用苹果LLVM 3.0编译器(w / o gdb -O0)和XCode 4.2使用Mac 64位应用程序花了多长时间。 我也通过仪器运行每个例子来大致了解记忆峰值。

以下每个示例都包含在以下代码块中:

 int main (int argc, const char * argv[]) { @autoreleasepool { NSDate *now = [NSDate date]; //Code Example ... NSTimeInterval interval = [now timeIntervalSinceNow]; printf("Duration: %f\n", interval); } } 

NSAutoreleasePool批量[原始预ARC](峰值内存:〜116 KB)

  static const NSUInteger BATCH_SIZE = 1500; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; for(uint32_t count = 0; count < MAX_ALLOCATIONS; count++) { NSString *text = [NSString stringWithFormat:@"%u", count + 1U]; [text class]; if((count + 1) % BATCH_SIZE == 0) { [pool drain]; pool = [[NSAutoreleasePool alloc] init]; } } [pool drain]; 

运行时间:
10.928158
10.912849
11.084716


外部@autoreleasepool(峰值内存:〜382 MB)

  @autoreleasepool { for(uint32_t count = 0; count < MAX_ALLOCATIONS; count++) { NSString *text = [NSString stringWithFormat:@"%u", count + 1U]; [text class]; } } 

运行时间:
11.489350
11.310462
11.344662


内部@autoreleasepool(最高内存:〜61.2KB)

  for(uint32_t count = 0; count < MAX_ALLOCATIONS; count++) { @autoreleasepool { NSString *text = [NSString stringWithFormat:@"%u", count + 1U]; [text class]; } } 

运行时间:
14.031112
14.284014
14.099625


@autoreleasepool w / goto(峰值内存:〜115KB)

  static const NSUInteger BATCH_SIZE = 1500; uint32_t count = 0; next_batch: @autoreleasepool { for(;count < MAX_ALLOCATIONS; count++) { NSString *text = [NSString stringWithFormat:@"%u", count + 1U]; [text class]; if((count + 1) % BATCH_SIZE == 0) { count++; //Increment count manually goto next_batch; } } } 

运行时间:
10.908756
10.960189
11.018382

goto声明提供了最接近的性能,但是它使用了goto 。 有什么想法吗?

更新:

注意: goto语句是@autoreleasepool的正常退出,如文档中所述,不会泄漏内存。

在进入时,推送一个自动释放池。 在正常退出(break,return,goto,fall-through等)时,autorelease池会popup。 为了与现有代码兼容,如果退出是由于例外,autorelease池不会popup。

以下应该达到与没有gotogoto答案相同的东西:

 for (NSUInteger count = 0; count < MAX_ALLOCATIONS;) { @autoreleasepool { for (NSUInteger j = 0; j < BATCH_SIZE && count < MAX_ALLOCATIONS; j++, count++) { NSString *text = [NSString stringWithFormat:@"%u", count + 1U]; [text class]; } } } 

请注意,ARC支持在-O0处未启用的重要优化。 如果要在ARC下测量性能,则必须在启用优化的情况下进行testing。 否则,您将在ARC的“朴素模式”下测量您手动调整的保留/释放位置。

再次运行您的testing与优化,看看会发生什么。

更新 :我很好奇,所以我自己跑了。 这些是发布模式(-Os)的运行时结果,具有7,000,000个分配。

 arc-perf[43645:f803] outer: 8.1259 arc-perf[43645:f803] outer: 8.2089 arc-perf[43645:f803] outer: 9.1104 arc-perf[43645:f803] inner: 8.4817 arc-perf[43645:f803] inner: 8.3687 arc-perf[43645:f803] inner: 8.5470 arc-perf[43645:f803] withGoto: 7.6133 arc-perf[43645:f803] withGoto: 7.7465 arc-perf[43645:f803] withGoto: 7.7007 arc-perf[43645:f803] non-ARC: 7.3443 arc-perf[43645:f803] non-ARC: 7.3188 arc-perf[43645:f803] non-ARC: 7.3098 

和记忆峰值(只有100,000分配运行,因为仪器是永远):

 Outer: 2.55 MB Inner: 723 KB withGoto: ~747 KB Non-ARC: ~748 KB 

这些结果让我感到吃惊。 那么,记忆高峰的结果不会; 这正是你所期望的。 但是,即使启用了优化, innerwithGoto之间的运行时间差也比我预期的要高。

当然,这是一个病态的微观testing,这是不太可能模拟任何应用程序的真实世界的性能。 这里要说的是,ARC可能确实有一定的开销,但是在做出假设之前,你应该总是测量你的实际应用。

(另外,我使用嵌套for循环testing了@ ipmcc的答案;它的行为几乎和goto版本一样。)