IT story

Clang vs GCC-더 나은 바이너리를 생성하는 방법은 무엇입니까?

hot-time 2020. 4. 17. 08:28
반응형

Clang vs GCC-더 나은 바이너리를 생성하는 방법은 무엇입니까? [닫은]


현재 GCC를 사용하고 있지만 최근 Clang을 발견했으며 전환을 숙고하고 있습니다. 바이너리의 품질 (속도, 메모리 풋 프린트, 신뢰성)이 결정하는 한 가지 요소 gcc -O3가 있습니다. 바이너리를 1 % 더 빠르게 실행하거나 1 % 더 적은 메모리를 사용하는 바이너리를 생성 할 수 있다면 , 그것은 획기적인 요소입니다.

Clang은 GCC보다 더 나은 컴파일 속도와 더 낮은 컴파일 타임 메모리 풋 프린트를 자랑하지만 결과 컴파일 소프트웨어의 벤치 마크 / 비교에 관심이 있습니다. 경험을 설명하거나 설명해 주시겠습니까?


다음은 GCC 4.7.2 및 Clang 3.2 C ++을 사용하여 좁은 결과를 얻었지만 최신 정보입니다.

업데이트 : GCC 4.8.1 v clang 3.3 비교가 아래에 추가되었습니다.

업데이트 : GCC 4.8.2 v clang 3.4 비교가 추가되었습니다.

GCC와 Clang 및 Windows 용 Microsoft 컴파일러를 사용하여 Linux 용으로 구축 된 OSS 도구를 유지 관리합니다. 툴 coan은 C / C ++ 소스 파일의 전 처리기 및 분석기이며 이러한 코드 라인은 재귀-강하 구문 분석 및 파일 처리에 대한 계산 프로파일 전공입니다. (이 결과와 관련된) 개발 지점은 현재 약 90 개의 파일로 약 11K LOC를 구성합니다. 이제 다형성과 템플릿이 풍부한 C ++로 코딩되었지만 여전히 해킹 된 C에서 그리 멀지 않은 많은 패치에 빠져 있습니다. 이동 의미는 명시 적으로 악용되지 않습니다. 단일 스레드입니다. "아키텍처"는 여전히 ToDo로 남아있는 동안 나는 그것을 최적화하기 위해 진지한 노력을 기울이지 않았다.

우수한 컴파일 속도와 진단에도 불구하고 C ++ 11 표준 지원은 coan이 수행하는 측면에서 현대 GCC 버전을 뒤엎었기 때문에 3.2 이전에는 Clang을 실험 컴파일러로만 사용했습니다. 3.2에서는이 차이가 사라졌습니다.

현재 코안 개발을위한 Linux 테스트 하네스는 한 파일 파서 테스트 사례, 1000 개 파일을 사용하는 스트레스 테스트 및 1K 미만의 파일을 사용하는 시나리오 테스트로 약 70K 소스 파일을 처리합니다. 테스트 결과를보고 할뿐 아니라 하네스는 소비 된 총 파일 수와 실행 시간을 coan으로 누적하여 표시합니다 (각 coan 명령 행을 Linux time명령으로 전달 하고보고 된 숫자를 추가하고 추가 함). 측정 가능한 시간이 0 인 테스트의 수는 모두 0에 가까워 지지만, 이러한 테스트의 기여는 무시할 수 있다는 사실에 의해 타이밍이 더욱 돋보입니다. 타이밍 통계는 make check다음과 같이 끝에 표시됩니다 .

coan_test_timer: info: coan processed 70844 input_files.
coan_test_timer: info: run time in coan: 16.4 secs.
coan_test_timer: info: Average processing time per input file: 0.000231 secs.

나는 테스트 하네스 성능을 GCC 4.7.2와 Clang 3.2 사이에서 비교했다. 컴파일러를 제외한 모든 것이 동일하다. Clang 3.2부터는 더 이상 GCC가 컴파일 할 코드 영역과 Clang 대안간에 전 처리기 차별화가 필요하지 않습니다. 각 경우에 동일한 C ++ 라이브러리 (GCC)에 구축하고 동일한 터미널 세션에서 모든 비교를 연속적으로 실행했습니다.

릴리스 빌드의 기본 최적화 수준은 -O2입니다. 또한 -O3에서 빌드를 성공적으로 테스트했습니다. 각 구성을 연속 3 회 테스트하고 3 가지 결과를 평균화하여 다음 결과를 얻었습니다. 데이터 셀의 숫자는 ~ 70K 입력 파일 (읽기, 파싱 및 쓰기 출력 및 진단)을 처리하기 위해 coan 실행 파일이 소비하는 평균 마이크로 초 수입니다.

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 231 | 237 |0.97 |
----------|-----|-----|-----|
Clang-3.2 | 234 | 186 |1.25 |
----------|-----|-----|------
GCC/Clang |0.99 | 1.27|

특정 응용 프로그램은 컴파일러의 장단점에 불공평하게 작용하는 특성을 가질 가능성이 높습니다. 엄격한 벤치마킹은 다양한 애플리케이션을 사용합니다. 이를 염두에두고이 데이터의 주목할만한 기능은 다음과 같습니다.

  1. -O3 최적화는 GCC에 거의 해를 끼쳤다
  2. -O3 최적화는 Clang에게 매우 유익했습니다.
  3. -O2 최적화에서 GCC는 수염만으로 Clang보다 빠릅니다.
  4. -O3 최적화에서 Clang은 GCC보다 훨씬 빠릅니다.

두 컴파일러의 흥미로운 비교는 그 발견 직후 우연히 발생했습니다. Coan은 스마트 포인터를 자유로이 사용하며 그 중 하나는 파일 처리에 많이 사용됩니다. 이 특정 스마트 포인터 유형은 컴파일러 차별화를 위해 이전 릴리스에서 typedef'd였으며 std::unique_ptr<X>, 구성된 컴파일러가 그 사용법에 대해 충분히 성숙한 지원을하는 경우, 그렇지 않으면 std::shared_ptr<X>. 바이어스의 정보는 다음의 제품에 std::unique_ptr이 포인터 주위에 전달 사실 이었기 때문에, 어리석은했지만, std::unique_ptr교체를 위해 벤치 옵션처럼 보였다 std::auto_ptr는 C ++ (11 개) 변종은 나에게 새로운있을 때 점에서.

Clang 3.2의 이와 유사한 차별화에 대한 지속적인 필요성을 측정하기위한 실험적 빌드 과정에서 필자는 실수로 빌드 std::shared_ptr<X>하려고 할 때 빌드 std::unique_ptr<X>했으며 기본 -O2 최적화를 사용하여 최종 실행 파일이 가장 빠르다는 사실에 놀랐습니다. 때로는 184 밀리 초를 달성했습니다. 입력 파일 당. 소스 코드를 한 번 변경하면 해당 결과는 다음과 같습니다.

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 234 | 234 |1.00 |
----------|-----|-----|-----|
Clang-3.2 | 188 | 187 |1.00 |
----------|-----|-----|------
GCC/Clang |1.24 |1.25 |

여기서 주목할 사항은 다음과 같습니다.

  1. 컴파일러는 이제 -O3 최적화에서 전혀 이점을 얻지 못합니다.
  2. Clang은 각 최적화 수준에서 GCC를 능가합니다.
  3. GCC의 성능은 스마트 포인터 유형 변경에 의해서만 영향을받습니다.
  4. Clang의 -O2 성능은 스마트 포인터 유형 변경의 영향을 크게받습니다.

이전과 스마트 포인터 타입 변경 후, 연타는 -O3 최적화에 실질적으로 빠른 coan 실행 파일을 구축 할 수 있으며, 그 포인터 형이 최고 일 때이 -O2 및 -O3에서 똑같이 빠른 실행 파일을 만들 수 있습니다 - std::shared_ptr<X>- 직업을 위해.

내가 언급 할 수없는 명백한 질문은 GCC가 무관심한 반면 많이 사용되는 스마트 포인터 유형이 고유에서 공유로 변경 될 때 Clang이 내 응용 프로그램에서 25 % -O2 속도를 찾을 수 있어야하는 이유 입니다 같은 변화에. 또한 Clang의 -O2 최적화가 스마트 포인터 선택의 지혜에 큰 감수성을 가지고 있다는 발견을 응원해야하는지, 부울 것인지도 모릅니다.

업데이트 : GCC 4.8.1 v clang 3.3

해당 결과는 다음과 같습니다.

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.1 | 442 | 443 |1.00 |
----------|-----|-----|-----|
Clang-3.3 | 374 | 370 |1.01 |
----------|-----|-----|------
GCC/Clang |1.18 |1.20 |

4 개의 실행 파일이 모두 1 개의 파일을 처리하는 데 이전보다 훨씬 더 많은 평균 시간이 걸린다는 사실 은 최신 컴파일러 성능에 반영 되지 않습니다 . 테스트 응용 프로그램의 이후 개발 지점은 그 동안 많은 구문 분석 정교함을 취해 속도를 높이기 때문입니다. 비율 만 중요합니다.

주목할 점은 이제 참신하지 않습니다.

  • GCC는 -O3 최적화에 무관심
  • clang은 -O3 최적화로 인한 이점이 거의 없음
  • clang은 각 최적화 수준에서 마찬가지로 중요한 마진만큼 GCC를 능가합니다.

이러한 결과를 GCC 4.7.2 및 clang 3.2의 결과와 비교하면 GCC가 각 최적화 수준에서 clang의 약 1/4을 차지했습니다. 그러나 테스트 응용 프로그램은 그 동안 크게 개발되었으므로 GCC의 코드 생성을 따라 잡을 수는 없습니다. (이번에는 타이밍을 얻은 응용 프로그램 스냅 숏에 주목하여 다시 사용할 수 있습니다.)

업데이트 : GCC 4.8.2 v clang 3.4

GCC 4.8.1 v Clang 3.3에 대한 업데이트를 완료하여 추가 업데이트를 위해 동일한 코인 스냅 샷을 고수한다고 말했습니다. 그러나 대신 해당 스냅 샷 (rev. 301) 최신 개발 스냅 샷에서 테스트 스위트 (rev. 619)를 통과하기로 결정했습니다. 이것은 결과에 약간의 경도를 제공하며 다른 동기가 있습니다.

원래의 게시물에 따르면 속도에 대한 코인을 최적화하는 데 노력을 기울이지 않았습니다. 이것은 여전히 ​​rev의 경우입니다. 301. 그러나 Coan 테스트 하니스에 타이밍 장치를 구축 한 후 테스트 스위트를 실행할 때마다 최신 변경의 성능 영향이 나를 쳐다 봤습니다. 나는 그것이 놀랍게도 크게 컸고, 기능의 향상으로 인해 느껴지는 것보다 경향이 훨씬 가파른 것을 보았다.

개정으로 308 테스트 스위트의 입력 파일 당 평균 처리 시간은 여기에 처음 게시 한 이후 두 배 이상이었습니다. 그 시점에서 나는 10 년 동안의 성과에 대해 귀찮게하지 않는 정책에 대해 U 턴을했습니다. 집중적 인 개정판에서는 619까지의 성능이 항상 고려되었으며 많은 수의 기본적으로 더 빠른 라인에서 핵심로드 베어러를 다시 작성했습니다 (비표준 컴파일러 기능을 사용하지 않더라도). 이 U 턴에 대한 각 컴파일러의 반응을 보는 것이 흥미로울 것입니다.

다음은 최신 두 컴파일러의 rev.301 빌드에 대해 잘 알려진 타이밍 매트릭스입니다.

coan-rev.301 결과

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 428 | 428 |1.00 |
----------|-----|-----|-----|
Clang-3.4 | 390 | 365 |1.07 |
----------|-----|-----|------
GCC/Clang | 1.1 | 1.17|

여기서 이야기는 GCC-4.8.1 및 Clang-3.3에서 약간 변경되었습니다. GCC의 쇼는 사소한 일입니다. Clang은 사소한 일입니다. 소음이이를 설명 할 수 있습니다. 연타는 여전히 앞두고 나오는 -O2-O3대부분의 응용 프로그램에 문제가되지 것입니다하지만 꽤 많은 상관 것이다 마진.

그리고 여기 rev의 행렬이 있습니다. 619.

coan-rev.619 결과

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 210 | 208 |1.01 |
----------|-----|-----|-----|
Clang-3.4 | 252 | 250 |1.01 |
----------|-----|-----|------
GCC/Clang |0.83 | 0.83|

301과 619 숫자를 나란히 취하면 몇 가지 요점이 있습니다.

  • 더 빠른 코드를 작성하려고했으며 두 컴파일러 모두 내 노력을 입증했습니다. 그러나:

  • GCC는 이러한 노력을 Clang보다 훨씬 관대하게 보상합니다. -O2최적화 연타의 619 빌드는 46 % 더 빠르게 자사의 301 빌드보다 :에서 -O3연타의 개선 31 %이다. 좋지만 각 최적화 수준에서 GCC의 619 빌드는 301보다 두 배 이상 빠릅니다.

  • GCC는 Clang의 이전 우월성을 역전시킵니다. 그리고 각 최적화 수준에서 GCC는 이제 Clang보다 17 % 뛰어납니다.

  • 301 빌드에서 Clang의 -O3최적화 에서 GCC보다 더 많은 레버리지를 얻는 기능은 619 빌드에서 사라졌습니다. 어느 컴파일러도 의미있는 것을 얻지 못합니다 -O3.

나는이 운이 뒤틀린 것에 대해 놀랐다. 나는 실수로 clang 3.4 자체를 느리게 만들었을지도 모른다고 생각했다. 그래서 나는 배포판의 주식 Clang 3.3으로 619 테스트를 다시 실행했습니다. 결과는 3.4와 실질적으로 동일합니다.

그래서 U 턴에 대한 반응과 관련하여 : 여기에 나와있는 숫자에서 Clang은 도움이되지 않을 때 C ++ 코드에서 속도를내는 속도로 GCC보다 훨씬 뛰어났습니다. 도움을 구할 때 GCC는 Clang보다 훨씬 나은 작업을 수행했습니다.

그 관찰을 원칙으로 높이지는 않지만 "어떤 컴파일러가 더 나은 바이너리를 생성합니까?"라는 교훈을 얻습니다. 대답은 상대적인 테스트 스위트를 지정하더라도 바이너리의 타이밍을 결정하는 명확한 문제는 아닙니다.

더 나은 바이너리가 가장 빠른 바이너리입니까, 아니면 값 싸게 제작 된 코드를 가장 잘 보상하는 바이너리입니까? 아니면 유지 관리 성을 우선시하고 속도를 넘어 재사용하는 값 비싼 코드를 가장 잘 보완 합니까? 바이너리를 생성하는 동기의 본질과 상대적 가중치와 그렇게하는 제약 조건에 달려 있습니다.

어쨌든 "최상의"바이너리를 만드는 것에 깊은 관심이 있다면, 연속적인 컴파일러 반복이 코드의 연속적인 반복보다 "최상의"라는 아이디어를 어떻게 전달하는지 더 잘 확인해야합니다.


Phoronix는 이에 대해 몇 가지 벤치 마크 를 수행했지만 몇 달 전 Clang / LLVM의 스냅 샷 버전에 관한 것입니다. 결과는 상황이 다소 밀려났다는 것입니다. 모든 경우에 GCC 나 Clang 모두 확실히 우수하지는 않습니다.

최신 Clang을 사용하기 때문에 관련성이 떨어질 수 있습니다. 다시, GCC 4.6은 Core 2와 i7에 대한 몇 가지 주요 최적화 를 갖도록 예정되어 있습니다 .

필자는 Clang의 빠른 컴파일 속도가 독창적 인 개발자에게는 더 좋을 것이라고 생각합니다. 그런 다음 코드를 세상에 밀어 넣으면 Linux distro / BSD / etc 등이 있습니다. 최종 사용자는 빠른 바이너리를 위해 GCC를 사용합니다.


Clang이 코드를 더 빠르게 컴파일한다는 사실은 결과 바이너리의 속도만큼 중요하지 않을 수 있습니다. 그러나 여기에 일련의 벤치 마크가 있습니다.


결과 바이너리의 속도 측면에서 GCC 4.8과 clang 3.3의 전체적인 차이는 거의 없습니다. 대부분의 경우 두 컴파일러에서 생성 한 코드는 비슷하게 수행됩니다. 이 두 컴파일러 중 어느 것도 다른 컴파일러를 지배하지 않습니다.

GCC와 clang간에 상당한 성능 차이가 있음을 나타내는 벤치 마크는 우연의 일치입니다.

프로그램 성능은 컴파일러의 선택에 영향을받습니다. 개발자 또는 개발자 그룹이 GCC를 독점적으로 사용하는 경우 프로그램은 clang보다 GCC에서 약간 더 빠르게 실행될 것으로 예상 될 수 있으며 그 반대도 마찬가지입니다.

개발자 관점에서 GCC 4.8+와 clang 3.3의 주목할만한 차이점은 GCC에 -Og명령 줄 옵션이 있다는 것입니다. 이 옵션은 디버깅을 방해하지 않는 최적화를 활성화하므로 예를 들어 항상 정확한 스택 추적을 얻을 수 있습니다. clang에이 옵션이 없으면 clang을 일부 개발자를위한 최적화 컴파일러로 사용하기가 더 어려워집니다.


이것을 결정하는 유일한 방법은 시도하는 것입니다. FWIW 필자는 일반 gcc 4.2 (많은 SSE가있는 x86-64 코드의 경우)와 비교하여 Apple의 LLVM gcc 4.2를 사용하여 실제로 개선되었지만 몇 가지 코드베이스에 대해서는 YMMV를 보았습니다. x86 / x86-64로 작업 중이고 마지막 몇 퍼센트를 실제로 신경 쓰고 있다고 가정하면 인텔의 ICC도 시도해야합니다. 이는 종종 gcc를 능가 할 수 있으므로 intel.com에서 30 일 평가판 라이센스를 얻을 수 있습니다 시도해보십시오.


gcc 5.2.1 및 clang 3.6.2에서 주목 한 독특한 차이점은 다음과 같은 중요한 루프가있는 경우입니다.

for (;;) {
    if (!visited) {
        ....
    }
    node++;
    if (!*node) break;
  }

그런 다음 gcc는 -O3or로 컴파일 할 때 -O2추측 적으로 루프를 8 번 풀립니다. Clang은 그것을 풀지 않을 것입니다. 시행 착오를 통해 내 프로그램 데이터의 특정 사례에서 올바른 언 롤링 횟수는 5이므로 gcc 오버 샷 및 클랜 언더 샷입니다. 그러나 오버 슛은 성능에 더 해 롭기 때문에 gcc는 훨씬 더 나빴습니다.

나는 없다 아무 생각 풀림 차이가 일반적인 경향이나 내 시나리오에 특이 뭔가 경우.

얼마 전에 저는 C에서 성능 최적화에 대해 더 많이 가르치기 위해 몇 가지 가비지 수집기작성 했습니다. 그리고 얻은 결과는 약간의 clang을 선호하기에 충분합니다. 가비지 수집은 대부분 포인터 추적 및 메모리 복사와 관련이 있기 때문입니다.

결과는 (초 단위의 숫자)입니다.

+---------------------+-----+-----+
|Type                 |GCC  |Clang|
+---------------------+-----+-----+
|Copying GC           |22.46|22.55|
|Copying GC, optimized|22.01|20.22|
|Mark & Sweep         | 8.72| 8.38|
|Ref Counting/Cycles  |15.14|14.49|
|Ref Counting/Plain   | 9.94| 9.32|
+---------------------+-----+-----+

이것은 모두 순수한 C 코드이며 C ++ 코드를 컴파일 할 때 컴파일러 성능에 대해 주장하지 않습니다.

Ubuntu 15.10, x86.64 및 AMD Phenom (tm) II X6 1090T 프로세서에서.


기본적으로 대답은 다음과 같습니다. 다양한 종류의 응용 프로그램에 중점을 둔 많은 벤치 마크가 있습니다.

내 앱의 벤치 마크는 gcc> icc> clang입니다.

IO는 드물지만 많은 CPU 플로트 및 데이터 구조 작업이 있습니다.

컴파일 플래그는 -Wall -g -DNDEBUG -O3입니다.

https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark

참고 URL : https://stackoverflow.com/questions/3187414/clang-vs-gcc-which-produces-better-binaries

반응형