2014-01-16 1 views
1

ARC 컴파일러에서 autorelease 풀 최적화가없는 이유는 무엇입니까? 가장 안쪽 범위에있는 객체를 유지하고 자동 풀에서 객체를 제거한 후 객체를 풀면 더 이상 사용하지 않습니까? 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

있습니다 위의 두 예제의 디스 어셈블리 결과. @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  
+0

alloc/initWith를 사용하는 경우 ... 릴리스를 삽입 할 수있는 기회가 있습니다.+ numberWith는 항상 자동 렌더링 된 객체를 반환합니다. 그래서 그것은 수영장에 의해 유지 될 것입니다. –

+0

@GradyPlayer 현재 작동 방식을 알고 있습니다. 개체가 풀에서 제거되지 않아서 범위가 끝나면 왜 풀어 낼 수 없는지 묻습니다. –

+1

ARC가 전혀 작동하지 않는다는 기적이라고 생각합니다. 니트를 뽑지 않죠. –

답변

2

"자동 풀 풀에서 제거"작업은 비효율적입니다. autorelease 풀은 나중에 릴리스 할 포인터의 배열입니다. 포인터가 풀에 있는지 확인하는 빠른 방법은 없습니다.

ARC에는 호출 수신자가 return [obj autorelease]을 수행 한 일부 사례에서 자동 해제를 제거 할 수있는 최적화 기능이 있습니다. 내 테스트에서 이것은 [NSNumber numberWithUnsignedInteger :]의 자동 해제 풀 오버 헤드를 0으로 줄입니다.

OS X 또는 iOS의 일부 버전에서는 ARC의 return-autorelease 최적화를 방해하는 방식으로 -numberWithUnsignedInteger :를 구현할 수 있습니다. 또한 반환 된 객체가 사용되지 않을 때 일부 컴파일러 버전이 return-autorelease 최적화를 수행하지 못할 수도 있습니다.

원래 테스트에서 NSLog()의 내부 구현은 자동 릴리즈 된 객체를 생성했습니다. ARC가 autorelease 풀의 모든 함수 호출이나 모든 루프를 래핑하지 못한다면 할 수있는 일은 없습니다.

+0

답변 해 주셔서 감사합니다. 실제로 메모리를 많이 늘리려면 NSLog 호출을 제거했습니다. x86_64에서 Xcode 5.1b2 및 iOS7.1SDK로 테스트했습니다. 자동 풀이있는 자동 포장은 어떻게됩니까? autorelease pool의 push/pop의 오버 헤드는 무엇입니까? –

+0

자동 복구 풀은 상대적으로 저렴하지만 사방에 추가 할만큼 저렴하지는 않습니다. 루프를 실행하는 함수의 릴리스 빌드를 디스 어셈블리 할 수 ​​있습니까? –

+0

질문 편집을 참조하십시오. –

2

왜 ARC 위의 코드를 최적화하지 않는 것을 믿습니까? 인스트루먼트 아래의 릴리스 모드로 시도해보십시오. 힙은 자랄 수 없습니다. 디버그에서 테스트하는 경우 문제는 옵티 마이저를 사용하지 않는 것입니다.

+0

릴리즈 모드에서'-Os' 최적화를 사용하면 메모리가 계속 증가합니다. –

+0

전체 요점을 게시 할 수 있습니까? 나는 메모리 증가없이 이것을 프로파일 링했습니다. https://gist.github.com/rnapier/8466934 –

0

아마도 코드를 호흡 할 시간을 줄 수있는 여러 건의 호출이있을 수 있습니다. 메소드를 한 번에 x 번 수행하는 것과 마찬가지로, 메소드가 리턴되면 그 메모리를 해제 할 가능성이 있습니다.

+0

이것은이 질문에 대한 답변이 아니며이 예에서는 잘못되었습니다. 이 예는 메모리를 빠르게 늘리고 요점을 보여주기위한 수단으로 주어졌습니다. –