다음은 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에서 그리 멀지 않은 많은 패치에 빠져 있습니다. 단일 스레드입니다. 나는 "아키텍처"가 크게 할일을 유지하면서 그것을 최적화하기 위해 진지한 노력을 기울이지 않았다.
뛰어난 컴파일 속도와 진단에도 불구하고 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|
특정 응용 프로그램은 컴파일러의 장단점에 불공평하게 작용하는 특성을 가질 가능성이 높습니다. 엄격한 벤치마킹은 다양한 애플리케이션을 사용합니다. 이를 염두에두고이 데이터의 주목할만한 기능은 다음과 같습니다.
- -O3 최적화는 GCC에 거의 해를 끼쳤다
- -O3 최적화는 Clang에게 매우 유익했습니다.
- -O2 최적화에서 GCC는 수염만으로 Clang보다 빠릅니다.
- -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 |
여기서 주목할 사항은 다음과 같습니다.
- 컴파일러는 이제 -O3 최적화에서 전혀 이점을 얻지 못합니다.
- Clang은 각 최적화 수준에서 GCC를 능가합니다.
- GCC의 성능은 스마트 포인터 유형 변경에 의해서만 영향을받습니다.
- 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보다 훨씬 나은 작업을 수행했습니다.
그 관찰을 원칙으로 높이지는 않지만 "어떤 컴파일러가 더 나은 바이너리를 생성합니까?"라는 교훈을 얻습니다. 대답은 상대적인 테스트 스위트를 지정하더라도 바이너리의 타이밍을 결정하는 명확한 문제는 아닙니다.
더 나은 바이너리가 가장 빠른 바이너리입니까, 아니면 값 싸게 제작 된 코드를 가장 잘 보상하는 바이너리입니까? 아니면
유지 관리 성을 우선시하고 속도를 넘어 재사용하는 값 비싼 코드를 가장 잘 보완 합니까? 바이너리를 생성하는 동기의 본질과 상대적 가중치와 그렇게하는 제약 조건에 달려 있습니다.
어쨌든 "최상의"바이너리를 만드는 것에 깊은 관심이 있다면, 연속적인 컴파일러 반복이 코드의 연속적인 반복보다 "최상의"라는 아이디어를 어떻게 전달하는지 더 잘 확인해야합니다.