수동 메모리 관리보다 가비지 수집 시연


23

가비지 수집 (이론적으로) 수동 메모리 관리보다 빠를 있는 많은 곳에서 읽었습니다 (허리, 심지어 직접 작성했습니다 ).

그러나, 보여주는 것보다 말하는 것이 쉽지 않습니다.
실제로이 효과가 실제로 나타나는 코드는 본 적이 없습니다.

이 성능 이점을 보여주는 코드를 가진 사람이 있습니까?


5
GC의 문제는 대부분의 구현이 실행이 매우 다른 결과를 가질 수 있도록 결정 아니라는 것이다,하지가 비교할 수있는 권리 변수를 분리하기 어렵다 언급
래칫 괴물

@ratchetfreak : 시간의 70 % 만 빠른 예제를 알고 있다면 저도 괜찮습니다. 적어도 처리량 측면에서 두 가지를 비교할 수 있는 방법 이 있어야합니다 (대기 시간이 작동하지 않을 수 있음).
Mehrdad

1
글쎄, 이것은 수동으로 GC가 수동으로했던 것보다 항상 수동으로 무엇이든 할 수 있기 때문에 약간 까다 롭습니다. 아마도 이것을 "표준"수동 메모리 관리 도구 (malloc () / free (), 소유 한 포인터, 참조 횟수가있는 공유 포인터, 약한 포인터, 사용자 지정 할당 자 없음)로 제한하는 것이 더 낫습니다. 또는 사용자 지정 할당자를 허용하는 경우 (어떤 프로그래머의 유형에 따라 더 현실적이거나 덜 현실적인 것일 수도 있음) 해당 할당 자에 대한 노력을 제한하십시오. 그렇지 않으면 수동 전략 "이 경우 GC가 수행하는 작업 복사"는 항상 GC만큼 빠릅니다.

1
"GC가하는 일을 복사"한다는 것은 "자신의 GC를 구축"한다는 의미는 아닙니다 (이것은 이론적으로 C ++ 11 이상에서 가능 하며 GC에 대한 선택적 지원 을 제공합니다). 나는 같은 의견에서 앞서 말했듯이 "GC가 수동으로 한 일보다 우위에있는 일을한다"는 것을 의미했다. 예를 들어, Cheney와 같은 압축이이 애플리케이션에 많은 도움이되는 경우, 포인터 수정을 처리하기 위해 사용자 정의 스마트 포인터를 사용하여 유사한 할당 + 압축 방식을 수동으로 구현할 수 있습니다. 또한 섀도 스택과 같은 기술을 사용하면 추가 작업을 희생하면서 C 또는 C ++에서 루트 찾기를 수행 할 수 있습니다.

1
@Ike : 괜찮습니다. 내가 왜 질문을했는지보십시오. 그것은 내 질문의 요점이었습니다. 사람들은 이해해야 할 모든 종류의 설명을 제시 하지만 실제로는 자신의 말이 옳다는 것을 증명하는 데모를 제공하도록 요청할 때 모든 사람들이 걸려 넘어집니다. 이 질문의 핵심은 실제로 실제로 일어날 수 있다는 것을 보여줍니다.
Mehrdad

답변:


26

http://blogs.msdn.com/b/ricom/archive/2005/05/10/416151.aspx를 참조 하고 모든 링크를 따라 Rico Mariani와 Raymond Chen (Microsoft의 유능한 프로그래머 모두)의 결의를 확인하십시오. . Raymond는 관리되지 않는 것을 개선하고 Rico는 관리되는 것과 동일한 것을 최적화하여 대응합니다.

본질적으로 최적화 노력이 필요하지 않기 때문에 관리되는 버전은 수동보다 몇 배나 빨리 시작되었습니다. 결국 매뉴얼은 관리 대상을 능가하지만 대부분의 프로그래머가 가고 싶지 않은 수준으로 최적화해야합니다. 모든 버전에서 매뉴얼의 메모리 사용량은 관리되는 것보다 훨씬 낫습니다.


실제 예를 인용 +1 코드를 C ++ 구조 (예 :의 적절한 사용은 비록 :) swap)하지 않는 것이 어렵다, 그리고 아마 아주 쉽게 성능 지혜가 당신을 얻을 것이다 ...
메흐 다드

5
성능면에서 Raymond Chen을 능가 할 수 있습니다. 나는 그가 아프기 때문에 그가 외면하지 않는 한, 수없이 열심히 일하고 있고, 운이 좋았다고 확신합니다. 왜 그가 당신이 선택한 솔루션을 선택하지 않았는지 모르겠습니다. 나는 그가 이유가 있다고 확신한다
btilly

Raymond의 코드를 여기에 복사 하고 비교하기 위해 여기 에 내 자신의 버전을 작성 했습니다 . 텍스트 파일이 포함 된 ZIP 파일은 여기에 있습니다 . 내 컴퓨터에서 광산은 14ms에서, Raymond는 21ms에서 실행됩니다. 나는 (가능하다) 뭔가 잘못했다하지 않는 한, 자신의 215 줄의 코드도 50 % 느리게 내 48 줄 구현보다 없이 메모리 매핑 된 파일 또는 사용자 정의 메모리 풀을 (그가 사용했던하는)를 사용. 광산은 C # 버전의 절반입니다. 내가 잘못 했습니까, 아니면 같은 일을 관찰합니까?
Mehrdad

1
@Mehrdad이 랩톱에서 gcc의 오래된 사본을 꺼내면 코드와 코드가 컴파일되지 않고 아무것도 할 수 없다고보고 할 수 있습니다. 내가 Windows에 있지 않다는 사실이 그 사실을 설명합니다. 그러나 숫자와 코드가 정확하다고 가정 해 봅시다. 그들은 10 년 된 컴파일러와 컴퓨터에서 동일한 성능을 발휘합니까? (블로그가 작성된 시점을 살펴보십시오.) 아마도 아닐 수도 있습니다. 그들이 (C 프로그래머 인) C ++을 올바르게 사용하는 방법을 모른다고 가정 해 봅시다. 우리는 무엇을 남겼습니까?
btilly

1
우리는 관리되는 메모리로 변환하고 속도를 높일 수있는 합리적인 C ++ 프로그램을 가지고 있습니다. 그러나 C ++ 버전을 최적화하고 더 멀리 가속화 할 수있는 곳. 우리 모두가 동의하는 것은 관리 코드가 관리되지 않는 것보다 빠를 때 항상 발생하는 일반적인 패턴입니다. 그러나 우리는 여전히 관리되는 버전에서 더 빠른 우수한 프로그래머의 합리적인 코드에 대한 구체적인 예를 가지고 있습니다.
btilly

5

경험상 무료 점심이 없다는 것입니다.

GC는 수동 메모리 관리의 어려움을 없애고 실수 할 가능성을 줄입니다. 특정 GC 전략이 문제에 대한 최적의 솔루션 인 상황이 있으며,이 경우 해당 전략을 사용하면 벌금이 부과되지 않습니다. 그러나 다른 솔루션이 더 빠른 다른 곳도 있습니다. 항상 낮은 수준에서 더 높은 추상화를 시뮬레이션 할 수 있지만 다른 방법으로는 불가능합니다. 일반적으로 높은 추상화가 낮은 것보다 빠를 수있는 방법이 없다는 것을 효과적으로 증명할 수 있습니다.

GC 수동 메모리 관리의 특별한 경우입니다

수동으로 더 나은 성능을 얻으려면 많은 작업이나 오류가 발생하기 쉽지만 다른 이야기입니다.


1
그건 말이되지 않습니다. 몇 가지 구체적인 예를 들자면 : 1) 프로덕션의 할당 자 및 쓰기 장벽은 C가 너무 비효율적이기 때문에 수작업으로 작성된 어셈블러입니다. C 컴파일러에서 수행하지 않는 고급 (기능) 언어로 수행되므로 C에서는 수행 할 수 없습니다. 스택 워킹은 고급 언어로 C 수준 아래에서 수행되는 작업의 또 다른 예입니다.
Jon Harrop

2
1) 주석을 달려면 특정 코드를 봐야하지만 어셈블러의 손으로 쓴 할당 자 / 장벽이 더 빠르면 손으로 쓴 어셈블러를 사용하십시오. 그것이 GC와 관련이 있는지 확실하지 않습니다. 2) 여기를 살펴보십시오 : stackoverflow.com/a/9814654/441099 요점은 일부 비 GC 언어가 꼬리 재귀 제거를 수행 할 수 있는지 여부는 아닙니다. 요점은 코드를 빠르고 빠르게 변환 할 수 있다는 것입니다. 특정 언어의 컴파일러가 자동으로이를 수행 할 수 있는지 여부는 편의상의 문제입니다. 충분히 낮은 추상화에서 원한다면 언제든지 직접 할 수 있습니다.
Guy Sirton

1
C의 꼬리 호출 예제는 함수 호출 자체의 특수한 경우에만 작동합니다. C는 호출하는 함수의 일반적인 경우를 처리 할 수 ​​없습니다. 튜링 타르타르는 어셈블러로 떨어지고 개발을위한 무한한 시간을 가정합니다.
Jon Harrop

3

GC가 수동 방법보다 훨씬 효율적으로 인위적인 상황을 구성하는 것은 쉽습니다. 가비지 수집기에는 하나의 "루트"만 있고 모든 것이 가비지이므로 GC 단계가 즉시 완료됩니다.

당신이 그것에 대해 생각하면, 그것은 프로세스에 할당 된 메모리를 가비지 수집 할 때 사용되는 모델입니다. 프로세스는 죽고, 모든 메모리는 가비지입니다. 실질적인 관점에서도 추적을 남기지 않고 시작, 실행 및 죽는 프로세스는 영원히 시작하고 실행하는 프로세스보다 더 효율적일 수 있습니다.

가비지 수집을 사용하여 언어로 작성된 실제 프로그램의 경우 가비지 수집의 이점은 속도가 아니라 정확성과 단순성입니다.


인공적인 예를 만드는 것이 쉬운 경우 간단한 예를 보여 주시겠습니까?
Mehrdad

1
@Mehrdad 그는 간단한 것을 설명했다. 종료하기 전에 GC 버전이 가비지 실행을 수행하지 못하는 프로그램을 작성하십시오. 수동 메모리 관리 버전은 물건을 명시 적으로 추적하고 비우기 때문에 속도가 느려집니다.
btilly

3
@btilly : "종료하기 전에 GC 버전에서 가비지 실행을 수행하지 못하는 프로그램을 작성하십시오." ... 처음에 가비지 수집을 수행하지 않으면 GC의 존재 로 인한 성능 향상이 아니라 작동하는 GC 가 없기 때문에 메모리 누수가 발생합니다 ! 프로그램이 종료되기 전에 C ++을 호출하는 것과 같습니다 . 무의미한 비교입니다. 가비지 수집조차하지 않고 메모리 누수가 발생합니다. 가비지 수집을 시작하지 않으면 가비지 수집이 더 빠르거나 느리다고 말할 수 없습니다.abort()
Mehrdad

극단적 인 예를 들기 위해서는 자신 만의 힙 및 힙 관리 기능을 갖춘 완전한 시스템을 정의해야합니다.이 시스템은 훌륭한 학생 프로젝트이지만이 마진에 맞지 않을 정도로 너무 큽니다. 비 GC 메모리 관리 방법에 스트레스를 주도록 설계된 방식으로 임의 크기의 배열을 할당하고 할당을 해제하는 프로그램을 작성하면 꽤 잘 수행됩니다.
ddyer

3
@Mehrdad 아닙니다. 시나리오는 GC 버전이 실행 된 임계 값에 도달하지 않았으며 다른 데이터 세트에서 올바르게 수행되지 않았기 때문입니다. GC 버전에는 그리 좋지 않을 것입니다. 그러나 최종 성능을 예측하는 것은 좋지 않습니다.
btilly

2

GC는 단순한 메모리 관리 전략이 아니라는 점을 고려해야합니다. 또한 언어 및 런타임 환경의 전체 디자인을 요구하여 비용 (및 이점)을 부과합니다. 예를 들어 GC를 지원하는 언어는 가비지 수집기에서 포인터를 숨길 수없고 일반적으로 신중하게 관리되는 시스템 기본 형식을 제외하고는 구성 할 수없는 형식으로 컴파일해야합니다. 또 다른 고려 사항은 GC가 완료까지 실행되어야하는 일부 단계를 부과하므로 응답 시간 보장을 유지하기가 어렵다는 것입니다.

결과적으로 가비지 수집 된 언어가 있고 동일한 시스템에서 수동으로 관리되는 메모리와 속도를 비교하는 경우 가비지 수집을 사용하지 않더라도 오버 헤드를 지불해야합니다.


2

빠를수록 모호합니다. 그러나 하드웨어가 지원되는 경우 초고속, 인식 할 수 없거나 더 빠를 수 있습니다. 오래 전에 LISP 기계에 대한 설계가있었습니다. 한 사람은 GC를 하드웨어의 메모리 하위 시스템에 내장하여 주 CPU가 그 CPU를 알지 못했습니다. 많은 다른 디자인과 마찬가지로 GC는 일시 중지가 거의 필요없이 메인 프로세서와 동시에 실행되었습니다. 보다 현대적인 디자인은 Azul Systems Vega 3 머신으로, JVM은 전용 프로세서와 일시 정지없는 GC를 사용하여 JVM보다 훨씬 빠르게 Java 코드를 실행합니다. GC (또는 Java)가 얼마나 빠른지 알고 싶다면 Google에 문의하십시오.


2

나는 이것에 대해 꽤 많은 작업을 수행했으며 여기 에 그 중 일부를 설명했습니다 . 나는 C ++에서 Boehm GC를 벤치마킹하여 목록 기반의 n- 퀸 솔버를 실행하는 OCaml의 주식 GC와 C ++로 작성된 사용자 지정 표시 영역 GC를 사용 malloc하지만 해제하지는 않지만 할당 및 해제를 사용하여 할당했습니다 free. OCaml의 GC는 모든 경우에 더 빠릅니다. C ++ 및 OCaml 프로그램은 의도적으로 동일한 할당을 동일한 순서로 수행하도록 작성되었습니다.

물론 64 비트 정수만 사용하고 할당은 사용하지 않고 문제를 해결하기 위해 프로그램을 다시 작성할 수 있습니다. 더 빠르기는하지만 연습의 요점을 잃을 것입니다 (새로운 GC 알고리즘의 성능을 예측하는 것이 C ++에 내장 된 프로토 타입을 사용하여 작업했습니다).

나는 실제 C ++ 코드를 관리되는 언어로 포팅하는 업계에서 수년을 보냈다. 거의 모든 단일 사례에서 나는 상당한 성능 향상을 관찰했으며, 그 중 많은 부분이 GC보다 수동 메모리 관리 때문일 것입니다. 실질적인 한계는 마이크로 벤치 마크에서 달성 할 수있는 것이 아니라 마감일 및 GC 기반 언어가 내가 결코 되돌아 보지 않은 생산성 향상을 제공하기 전에 달성 할 수있는 것입니다. 임베디드 장치 (마이크로 컨트롤러)에서 여전히 C 및 C ++을 사용하지만 지금도 바뀌고 있습니다.


+1 감사합니다. 벤치 마크 코드는 어디에서보고 실행할 수 있습니까?
Mehrdad

코드는 장소에 흩어져 있습니다. 마크 지역 버전을 여기에 게시했습니다 : groups.google.com/d/msg/…
Jon Harrop

1
스레드 안전과 안전하지 않은 결과가 있습니다.
Jon Harrop

1
@Mehrdad : "이러한 잠재적 오류 원인을 제거 했습니까?" 예. OCaml에는 이스케이프 분석과 같은 최적화가없는 매우 간단한 컴파일 모델이 있습니다. 클로저에 대한 OCaml의 표현은 실제로 C ++ 솔루션보다 실질적으로 느리므로 C ++ List.filter처럼 사용자 정의 를 사용해야합니다 . 그러나 일부 RC 작업을 생략 할 수 있다는 것은 확실합니다. 그러나 야생에서 볼 수있는 가장 큰 문제는 사람들이 대규모 산업 코드 기반에서 직접 이러한 최적화를 수행 할 시간이 없다는 것입니다.
Jon Harrop

2
네 그럼요. 코드를 작성하는 데 추가 노력을 기울이지 않아도 C ++의 병목 현상은 아닙니다. 코드를 유지 관리하는 것입니다. 이러한 종류의 부수적 인 복잡성으로 코드를 유지 관리하는 것은 악몽입니다. 대부분의 산업 코드 기반은 수백만 줄의 코드입니다. 당신은 그걸 다루고 싶지 않습니다. 사람들이 shared_ptr동시성 버그를 수정하기 위해 모든 것을 변환하는 것을 보았습니다 . 코드는 훨씬 느리지 만 이제는 작동합니다.
Jon Harrop

-1

이러한 예에는 반드시 수동 메모리 할당 체계가 잘못되어 있습니다.

최고의 가비지 수집기를 가정하십시오 GC. 내부적으로 메모리를 할당하고 해제 할 수있는 메모리를 결정하는 방법과 최종적으로 해제 할 수있는 방법이 있습니다. 이들 모두는 모두보다 시간이 덜 걸립니다 GC. 시간이 다른 방법에 사용 GC됩니다.

이제 동일한 할당과 같은기구를 사용하는 수동 할당을 고려 GC하고, free()통화 바로 옆 메모리를 설정하는 방법과 동일한 방법에 의해 해제되는이 GC. 스캔 단계가 없으며 다른 방법도 없습니다. 시간이 덜 걸립니다.


2
가비지 콜렉터는 종종 메모리를 유용한 상태로 두지 않고도 많은 오브젝트를 해제 할 수 있습니다. 특정 기준에 맞는 모든 항목을 배열 목록에서 제거하는 작업을 고려하십시오. N- 항목 목록에서 단일 항목을 제거하는 것은 O (N)입니다. N 목록에서 M 항목을 한 번에 하나씩 제거하면 O (M * N)입니다. 그러나 목록을 한 번에 통과하여 기준을 충족하는 모든 항목을 제거하는 것은 O (1)입니다.
supercat

@supercat : 배치도 free수집 할 수 있습니다. (물론 목록 순회 자체로 인해 기준을 충족하는 모든 항목을 제거하는 것은 여전히 ​​O (N)입니다)
MSalters

기준을 충족하는 모든 항목을 제거하는 단계이며 , 적어도 O (N). 당신은 올바른있어 free각 메모리 항목과 연관된 플래그가 있다면 GC는 여전히 상황에서 앞으로 나올 수 있지만, 일괄 수집 모드에서 작동 할 수있다. 하나의 N 사물 중에서 L 개의 구별되는 항목을 식별하는 M 참조가있는 경우, 참조가 존재하지 않는 모든 참조를 제거하고 나머지를 통합하는 시간은 O (N)이 아니라 O (M)입니다. 사용 가능한 M 공간이 더 있으면 스케일링 상수가 매우 작을 수 있습니다. 또한, 비 스캐닝 GC 시스템의 소형화는 ... 필요
supercat

@ supercat : 글쎄, 첫 번째 주석 상태에서 마지막 문장으로 O (1)이 아닙니다.
MSalters

1
@MSalters : "그리고 결정 론적 체계가 보육원을 갖지 못하게하는 것은 무엇입니까?" 아무것도. OCaml의 추적 가비지 수집기는 결정 론적이며 보육원을 사용합니다. 그러나 그것은 "수동적"이 아니며 "결정적"이라는 단어를 잘못 사용하고 있다고 생각합니다.
Jon Harrop
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.