ARC编译器缺乏自动释放优化

时间:2014-01-16 20:20:21

标签: objective-c automatic-ref-counting llvm

我只是想知道,为什么ARC编译器下没有自动释放池优化,它会在最里面的范围内保留一个对象,从自动释放池中删除它并在对象不再使用后释放?

引用另一个问题的一个非常不切实际的例子,

for(NSUInteger i = 0; i < 10000; i++)
{
    for(NSUInteger j = 0; j < 10000; j++)
    {
        NSNumber* n = [NSNumber numberWithUnsignedInteger:j];
        //NSLog(@"%@", n); //Disabled this to increase memory bloat faster.
    }
}

没有@autoreleasepool { ... }包装,内存会增长并增长。使用@autoreleasepool进行换行,内存仍然很低:

for(NSUInteger i = 0; i < 10000; i++)
{
    for(NSUInteger j = 0; j < 10000; j++)
    {
        @autoreleasepool {
            NSNumber* n = [NSNumber numberWithUnsignedInteger:j];
            //NSLog(@"%@", n); //Disabled this to increase memory bloat faster.
        }
    }
}

但是为什么编译器不能优化像这样的情况,在最内层范围之外不需要对象并且不需要@autoreleasepool包装?是否有技术原因这是不可能的或尚未完成的?

修改

为了澄清,为什么编译器不能输出如下代码:

for(NSUInteger i = 0; i < 10000; i++)
{
    for(NSUInteger j = 0; j < 10000; j++)
    {
        NSNumber* n = [NSNumber numberWithUnsignedInteger:j];
        objc_retain(n);
        objc_removeFromAutoreleasePool(n);
        NSLog(@"%@", n);
        objc_release(n);
    }
}

修改2

应Greg的要求,以下是上述两个示例的反汇编结果。

没有@autoreleasepool { }

TestOpt`-[LMViewController testAutoreleaseMem] at LMViewController.m:17:
0x2187:  pushl  %ebp
0x2188:  movl   %esp, %ebp
0x218a:  pushl  %ebx
0x218b:  pushl  %edi
0x218c:  pushl  %esi
0x218d:  subl   $0x1c, %esp
0x2190:  calll  0x2195                    ; -[LMViewController testAutoreleaseMem] + 14 at LMViewController.m:17
0x2195:  popl   %esi
0x2196:  xorl   %eax, %eax
0x2198:  movl   0x13cb(%esi), %ebx
0x219e:  movl   %eax, -0x10(%ebp)
0x21a1:  xorl   %edi, %edi
0x21a3:  movl   0x13df(%esi), %eax
0x21a9:  movl   %edi, 0x8(%esp)
0x21ad:  movl   %ebx, 0x4(%esp)
0x21b1:  movl   %eax, (%esp)
0x21b4:  calll  0x227e                    ; symbol stub for: objc_msgSend
0x21b9:  movl   %eax, (%esp)
0x21bc:  calll  0x2296                    ; symbol stub for: objc_retainAutoreleasedReturnValue
0x21c1:  movl   %eax, (%esp)
0x21c4:  calll  0x228a                    ; symbol stub for: objc_release
0x21c9:  incl   %edi
0x21ca:  cmpl   $0x2710, %edi
0x21d0:  jne    0x21a3                    ; -[LMViewController testAutoreleaseMem] + 28 at LMViewController.m:24
0x21d2:  movl   -0x10(%ebp), %eax
0x21d5:  incl   %eax
0x21d6:  cmpl   $0x2710, %eax
0x21db:  jne    0x219e                    ; -[LMViewController testAutoreleaseMem] + 23 at LMViewController.m:24
0x21dd:  addl   $0x1c, %esp
0x21e0:  popl   %esi
0x21e1:  popl   %edi
0x21e2:  popl   %ebx
0x21e3:  popl   %ebp
0x21e4:  ret    

使用:

TestOpt`-[LMViewController testAutoreleaseMem] at LMViewController.m:17:
0x216f:  pushl  %ebp
0x2170:  movl   %esp, %ebp
0x2172:  pushl  %ebx
0x2173:  pushl  %edi
0x2174:  pushl  %esi
0x2175:  subl   $0x1c, %esp
0x2178:  calll  0x217d                    ; -[LMViewController testAutoreleaseMem] + 14 at LMViewController.m:17
0x217d:  popl   %ecx
0x217e:  movl   %ecx, -0x10(%ebp)
0x2181:  xorl   %eax, %eax
0x2183:  movl   0x13e3(%ecx), %ecx
0x2189:  movl   %eax, -0x14(%ebp)
0x218c:  xorl   %edi, %edi
0x218e:  movl   %ecx, %ebx
0x2190:  calll  0x2278                    ; symbol stub for: objc_autoreleasePoolPush
0x2195:  movl   %eax, %esi
0x2197:  movl   -0x10(%ebp), %eax
0x219a:  movl   0x13f7(%eax), %eax
0x21a0:  movl   %edi, 0x8(%esp)
0x21a4:  movl   %ebx, 0x4(%esp)
0x21a8:  movl   %eax, (%esp)
0x21ab:  calll  0x227e                    ; symbol stub for: objc_msgSend
0x21b0:  movl   %eax, (%esp)
0x21b3:  calll  0x2296                    ; symbol stub for: objc_retainAutoreleasedReturnValue
0x21b8:  movl   %eax, (%esp)
0x21bb:  calll  0x228a                    ; symbol stub for: objc_release
0x21c0:  movl   %esi, (%esp)
0x21c3:  calll  0x2272                    ; symbol stub for: objc_autoreleasePoolPop
0x21c8:  incl   %edi
0x21c9:  cmpl   $0x2710, %edi
0x21cf:  jne    0x2190                    ; -[LMViewController testAutoreleaseMem] + 33 at LMViewController.m:23
0x21d1:  movl   %ebx, %ecx
0x21d3:  movl   -0x14(%ebp), %eax
0x21d6:  incl   %eax
0x21d7:  cmpl   $0x2710, %eax
0x21dc:  jne    0x2189                    ; -[LMViewController testAutoreleaseMem] + 26 at LMViewController.m:24
0x21de:  addl   $0x1c, %esp
0x21e1:  popl   %esi
0x21e2:  popl   %edi
0x21e3:  popl   %ebx
0x21e4:  popl   %ebp
0x21e5:  ret    

3 个答案:

答案 0 :(得分:2)

为什么您认为ARC不优化上述代码?在仪器下的释放模式下尝试。堆不会增长。如果你在Debug下进行测试,那么问题是你没有使用优化器。

答案 1 :(得分:2)

任何“从自动释放池中删除”操作都会效率低下。自动释放池只是一个稍后要释放的指针数组。没有快速的方法来检查池中是否存在指针。

ARC确实有一个优化可以从被调用者执行return [obj autorelease]的某些情况中删除自动释放。在我的测试中,这将 - [NSNumber numberWithUnsignedInteger:]的自动释放池开销减少到零。

某些版本的OS X或iOS可能会实现-numberWithUnsignedInteger:阻止ARC的返回自动释放优化。当返回的对象未使用时,某些编译器版本也可能无法执行返回自动释放优化。

在原始测试中,NSLog()的内部实现生成了自动释放的对象。 ARC无法解决每个函数调用或自动释放池中的每个循环的问题。

答案 2 :(得分:0)

您可能会将功能分解为多个调用,这会让代码有时间呼吸。就像有一个方法一次执行x,并且当该方法返回时,它可能会释放该内存。