Linux에서 실행되는 C ++ 코드를 어떻게 프로파일 링 할 수 있습니까?


1816

Linux에서 실행되는 C ++ 응용 프로그램이 있는데 최적화 과정에 있습니다. 코드의 어느 영역이 느리게 실행되고 있는지 어떻게 알 수 있습니까?


27
개발 스택에 대한 더 많은 데이터를 제공하면 더 나은 답변을 얻을 수 있습니다. 인텔과 썬의 프로파일 러가 있지만 컴파일러를 사용해야합니다. 그게 옵션인가요?
Nazgob

2
그것은 이미 다음 링크에 대한 답변 : stackoverflow.com/questions/2497211/...을
카필 굽타을

4
대부분의 답변은 code프로파일 러입니다. 그러나 우선 순위 반전, 캐시 앨리어싱, 리소스 경합 등은 모두 최적화 및 성능의 요소가 될 수 있습니다. 사람들이 내 느린 코드 로 정보를 읽는다고 생각합니다 . FAQ는이 스레드를 참조하고 있습니다.
artless noise


3
나는 pstack을 무작위로 사용했었다. 대부분의 시간은 프로그램이 가장 많은 곳에서 가장 일반적인 스택을 출력하므로 병목 현상을 지적한다.
호세 마누엘 고메즈 알바레즈

답변:


1406

프로파일 러를 사용하는 것이 목표라면 제안 된 프로파일 러 중 하나를 사용하십시오.

그러나 서두르고 주관적으로 느리게 진행되는 동안 디버거에서 프로그램을 수동으로 중단 할 수있는 경우 성능 문제를 찾는 간단한 방법이 있습니다.

여러 번 중지하고 매번 호출 스택을보십시오. 시간의 일부, 20 % 또는 50 %를 낭비하는 코드가있는 경우 각 샘플의 동작에서 코드를 잡을 확률이 있습니다. 이것은 대략 샘플의 비율입니다. 교육받은 추측은 필요하지 않습니다. 문제가 무엇인지 추측하면 문제를 증명하거나 반증합니다.

크기가 다른 여러 성능 문제가있을 수 있습니다. 그중 하나를 청소하면 나머지 패스는 나머지 패스에서 더 큰 비율을 차지하고 쉽게 찾을 수 있습니다. 이 확대 효과 는 여러 문제로 인해 복합화 될 때 실제로 막대한 속도 향상 요소로 이어질 수 있습니다.

주의 사항 : 프로그래머는 스스로 사용하지 않는 한이 기술에 회의적입니다. 프로파일 러가이 정보를 제공한다고 말하지만 전체 호출 스택을 샘플링 한 다음 임의의 샘플 세트를 검사 할 수있는 경우에만 해당됩니다. (요약은 통찰력을 잃는 곳입니다.) 호출 그래프는 동일한 정보를 제공하지 않습니다.

  1. 그들은 수업 수준에서 요약하지 않으며
  2. 재귀가있을 때 혼란스러운 요약을 제공합니다.

또한 실제로는 모든 프로그램에서 작동 할 때 장난감 프로그램에서만 작동한다고 말하고 더 큰 프로그램에서 더 잘 작동하는 것으로 보입니다. 그들은 때때로 문제가되지 않는 것을 발견한다고 말할 것입니다. 그러나 당신이 무언가를 한 번 본다면 그것은 사실 입니다. 둘 이상의 샘플에 문제가 있으면 실제로 발생합니다.

PS Java에서와 같이 특정 시점에 스레드 풀의 콜 스택 샘플을 수집하는 방법이있는 경우 다중 스레드 프로그램에서도 수행 할 수 있습니다.

PPS 대략적으로, 소프트웨어에 추상화 계층이 많을수록 성능 문제의 원인이되고 속도를 높일 수있는 기회가 될 가능성이 높습니다.

추가 : 분명하지는 않지만 스택 샘플링 기술은 재귀가있을 때 똑같이 잘 작동합니다. 그 이유는 명령을 제거하여 저장되는 시간은 샘플 내에서 발생할 수있는 횟수에 관계없이 명령을 포함하는 샘플의 비율에 의해 근사되기 때문입니다.

내가 종종 듣게되는 또 다른 반대는 " 임의의 장소를 무작위로 멈출 것이고 실제 문제를 놓치게 될 것이다 "입니다. 이것은 실제 문제가 무엇인지에 대한 사전 개념을 가지고 있습니다. 성능 문제의 주요 속성은 기대를 무시한다는 것입니다. 샘플링은 문제가 있다고 말하고 첫 번째 반응은 불신입니다. 그것은 당연하지만, 문제가 발견되면 그것이 진짜인지, 그 반대인지를 확신 할 수 있습니다.

추가 : 작동 방식에 대한 베이지안 설명을하겠습니다. I호출 스택에 약간 f의 시간이 걸리므로 (많은 비용이 소요되는 ) 명령 (호출 또는 기타) 이 있다고 가정하십시오 . 간단히하기 위해, 우리가 무엇인지 f모르지만 그것이 0.1, 0.2, 0.3, ... 0.9, 1.0이고 각각의 가능성에 대한 사전 확률이 0.1이라고 가정하면, 모든 비용이 동등하게 선험적으로.

그런 다음 스택 샘플을 2 개만 가져 와서 I관찰로 지정된 두 샘플에 대한 명령 을 봅니다 o=2/2. 이것 에 따르면 의 빈도 f에 대한 새로운 추정치를 제공 I합니다.

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&&f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.1    1     1             0.1          0.1            0.25974026
0.1    0.9   0.81          0.081        0.181          0.47012987
0.1    0.8   0.64          0.064        0.245          0.636363636
0.1    0.7   0.49          0.049        0.294          0.763636364
0.1    0.6   0.36          0.036        0.33           0.857142857
0.1    0.5   0.25          0.025        0.355          0.922077922
0.1    0.4   0.16          0.016        0.371          0.963636364
0.1    0.3   0.09          0.009        0.38           0.987012987
0.1    0.2   0.04          0.004        0.384          0.997402597
0.1    0.1   0.01          0.001        0.385          1

                  P(o=2/2) 0.385                

마지막 열은 예를 들어 f60보다 큰 사전 확률보다 > = 0.5 일 확률 이 92 % 라고 말합니다 .

이전의 가정이 다르다고 가정하십시오. P(f=0.1).991 (거의 확실 함) 이라고 가정 하고 다른 모든 가능성은 거의 불가능하다고 가정합니다 (0.001). 다시 말해, 우리의 이전 확실성은 그것이 I싸다 는 것 입니다. 그러면 우리는 다음을 얻습니다.

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&& f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.001  1    1              0.001        0.001          0.072727273
0.001  0.9  0.81           0.00081      0.00181        0.131636364
0.001  0.8  0.64           0.00064      0.00245        0.178181818
0.001  0.7  0.49           0.00049      0.00294        0.213818182
0.001  0.6  0.36           0.00036      0.0033         0.24
0.001  0.5  0.25           0.00025      0.00355        0.258181818
0.001  0.4  0.16           0.00016      0.00371        0.269818182
0.001  0.3  0.09           0.00009      0.0038         0.276363636
0.001  0.2  0.04           0.00004      0.00384        0.279272727
0.991  0.1  0.01           0.00991      0.01375        1

                  P(o=2/2) 0.01375                

이제는 P(f >= 0.5)0.6 %의 이전 가정보다 26 % 라고 합니다. 따라서 Bayes를 사용하면 예상 비용의 추정치를 업데이트 할 수 있습니다 I. 데이터의 양이 적 으면 비용이 얼마인지 정확하게 알려주지 않고 수정해야 할만큼 큰 것만 알려줍니다.

또 다른 방법으로 승계 규칙 이라고합니다 . 동전을 두 번 뒤집어서 두 번 머리를 올리면 동전의 가능한 무게에 대해 무엇을 알 수 있습니까? 존중받는 대답은 평균값을 가진 베타 배포판이라고 말하는 것입니다 (number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%.

(핵심은 우리가 I두 번 이상 보는 것입니다. 한 번만 보는 경우 f> 0을 제외하고는 많은 것을 알려주지 않습니다. )

따라서 아주 적은 수의 샘플조차도 지침 비용에 대해 많은 것을 알 수 있습니다. (그리고 만약 그것이. 원가에 비례 평균 주파수 그들을 볼 n샘플을 촬영하고, f그 후, 비용은 I에 나타날 것이다 nf+/-sqrt(nf(1-f))샘플. 예 n=10, f=0.33+/-1.4샘플).


추가 : 측정 및 임의 스택 샘플링의 차이에 대한 직관적 인 느낌을주고 :
프로파일이 있습니다 지금 샘플도 벽 시계 시간에 스택,하지만 무엇을 제공하는 것입니다 측정 (또는 핫 경로 또는 핫 스팟, 어떤에서 "병목 현상"은 쉽게 숨길 수 있습니다). 그들이 당신에게 보여주지 않는 (그리고 그들은 쉽게 할 수있는) 실제 샘플 자체입니다. 병목 현상 을 찾는 것이 목표라면 평균적 으로 2를 소요 시간의 일부로 나눈 값 을 볼 수 있습니다 . 따라서 30 %의 시간이 걸리면 평균적으로 2 / .3 = 6.7 샘플이 표시되며 20 샘플이 표시 할 확률은 99.2 %입니다.

다음은 측정 검사와 스택 샘플 검사의 차이점에 대한 설명입니다. 병목 현상은 이와 같은 하나의 큰 얼룩이거나 수많은 작은 것이므로 차이가 없습니다.

여기에 이미지 설명을 입력하십시오

측정은 수평입니다. 특정 루틴에 소요되는 시간을 알려줍니다. 샘플링은 수직입니다. 전체 프로그램이 그 순간에 수행하는 작업을 피할 수있는 방법이 있고 두 번째 샘플 에서 볼 경우 병목 현상을 발견했습니다. 그것이 차이를 만드는 이유입니다. 시간이 얼마나 걸리는가에 대한 전체 이유를 보는 것입니다.


292
이것은 기본적으로 가난한 사람의 샘플링 프로파일 러이지만 훌륭하지만 샘플 크기가 너무 작아 위험 할 수 있습니다.
Crashworks

100
@Crash : "가난한 사람"부분에 대해서는 토론하지 않겠다 나는 후자에 초점을 맞추고 있는데, 측정의 정밀도가 아니라 위치의 정밀도가 필요합니다. 예를 들어 스택 중간에 단일 함수 호출 A ()가있을 수 있습니다. 그것은 시간의 50 %를 차지하지만 비용이 많이 들지 않는 A ()에 대한 많은 다른 호출과 함께 다른 큰 함수 B에있을 수 있습니다. 함수 시간의 정확한 요약은 실마리가 될 수 있지만 다른 모든 스택 샘플은 문제를 정확하게 지적합니다.
Mike Dunlavey

41
... 세계는 통화 횟수 및 / 또는 평균 타이밍으로 주석이 달린 통화 그래프가 충분하다고 생각하는 것 같습니다. 그렇지 않습니다. 그리고 슬픈 부분은 콜 스택을 샘플링하는 사람들에게는 가장 유용한 정보가 바로 앞에 있지만 "통계"의 이익을 위해 그것을 버린다는 것입니다.
Mike Dunlavey가

30
나는 당신의 기술에 동의하지 않습니다. 분명히 나는 ​​스택 워킹 샘플링 프로파일 러에 크게 의존합니다. 방금 자동화 된 방식으로 수행하는 몇 가지 도구가 있음을 지적합니다 .25 %에서 15 %의 함수를 얻는 시점을 지나서 1.2 %에서 0.6 %
Crashworks

13
-1 : 깔끔한 아이디어이지만, 약간의 성능 지향적 인 환경에서 일하기 위해 돈을받는다면 이것은 모두의 시간 낭비입니다. 실제 프로파일 러를 사용하여 실제 문제를 해결하지 않아도됩니다.
Sam Harwell

583

다음 옵션과 함께 Valgrind 를 사용할 수 있습니다

valgrind --tool=callgrind ./(Your binary)

라는 파일을 생성합니다 callgrind.out.x. 그런 다음 kcachegrind도구를 사용 하여이 파일을 읽을 수 있습니다 . 어떤 라인에 얼마의 비용이 드는지와 같은 결과로 사물의 그래픽 분석을 제공합니다.


51
valgrind는 훌륭하지만, 프로그램을 느리게 만들 것이라는 경고를
받습니다

30
출력을 시각화하는 놀라운 대안 방법 은 Gprof2Dot 도 확인하십시오 . ./gprof2dot.py -f callgrind callgrind.out.x | dot -Tsvg -o output.svg
Sebastian

2
@neves 예 Valgrind는 "gstreamer"및 "opencv"응용 프로그램을 실시간으로 프로파일 링하는 데있어 속도면에서 그다지 도움이되지 않습니다.
enthusiasticgeek

1
stackoverflow.com/questions/375913/… 는 속도 문제에 대한 부분적인 해결책입니다.
Tõnu Samuel

3
@Sebastian : gprof2dot지금 여기에 있습니다 : github.com/jrfonseca/gprof2dot
존 Zwinck

348

GCC를 사용한다고 가정합니다. 표준 솔루션은 gprof 로 프로파일 링하는 것 입니다.

-pg프로파일 링 전에 컴파일 에 추가해야합니다 .

cc -o myprog myprog.c utils.c -g -pg

아직 시도하지 않았지만 google-perftools 에 대한 좋은 소식을 들었습니다 . 시도해 볼 가치가 있습니다.

관련 질문은 여기에 있습니다 .

Valgrind , Intel VTune , Sun DTracegprof : 당신을 위해 일을하지 않으면 몇 가지 다른 유행어 .


3
나는 gprof가 현재 표준이라는 것에 동의합니다. 그러나 Valgrind는 속도 최적화가 아닌 메모리 누수 및 프로그램의 기타 메모리 관련 측면을 프로파일 링하는 데 사용됩니다.
Bill the Lizard

68
빌, vaglrind suite에서 callgrind와 massif를 찾을 수 있습니다. 모두 프로파일 애플리케이션에 매우 유용합니다
다리오 minonne

7
@ Bill-the-Lizard : gprof에 대한 의견 : stackoverflow.com/questions/1777556/alternatives-to-gprof/…
Mike Dunlavey

6
gprof -pg는 콜 스택 프로파일 링의 근사치입니다. mcount 호출을 삽입하여 어떤 함수가 다른 함수를 호출하는지 추적합니다. 시간에 대한 표준 시간 기반 샘플링을 사용합니다. 그런 다음 호출 횟수에 비례하여 foo () 함수에서 샘플링 된 시간을 foo ()의 호출자에게 다시 할당합니다. 따라서 비용이 다른 통화를 구분하지 않습니다.
Krazy Glew

1
clang / clang ++를 사용하면 gperftools 의 CPU 프로파일 러 사용을 고려할 수 있습니다 . 경고 : 그렇게하지 않았습니다.
einpoklum

257

최신 커널 (예 : 최신 Ubuntu 커널)에는 새로운 'perf'도구 ( apt-get install linux-tools) AKA perf_events가 제공 됩니다.

여기 에는 멋진 타임 차트 뿐만 아니라 클래식 샘플링 프로파일 러 ( 맨 페이지 )가 함께 제공됩니다 !

중요한 것은 이러한 도구가 프로세스 프로파일 링이 아니라 시스템 프로파일 링 이 될 수 있다는 것입니다. 스레드, 프로세스 및 커널 간의 상호 작용을 보여주고 프로세스 간의 스케줄링 및 I / O 종속성을 이해할 수 있습니다.

대체 텍스트


12
훌륭한 도구! 어쨌든 "main-> func1-> fun2"스타일로 시작하는 전형적인 "나비"보기를 얻을 수 있습니까? 나는 그것을 알아낼 수없는 ... perf report호출 부모와 함께 기능 이름을 제공하는 것 같습니다 ... (그래서 역 나비 모양의 일종)
kizzx2

윌, 스레드 활동의 시간표를 표시 할 수 있습니다. CPU 번호 정보가 추가 되었습니까? 모든 CPU에서 언제 어떤 스레드가 실행되고 있는지보고 싶습니다.
osgx

2
@ kizzx2- gprof2dot및 을 사용할 수 있습니다 perf script. 아주 좋은 도구!
dashesy

2
4.13과 같은 최신 커널조차도 프로파일 링을위한 eBPF가 있습니다. 참조 brendangregg.com/blog/2015-05-15/ebpf-one-small-step.htmlbrendangregg.com/ebpf.html
앤드류 스턴

또 다른 좋은 소개 perf에 존재 archive.li/9r927#selection-767.126-767.271 합니다 (SO 신들은 SO 지식베이스에서 해당 페이지를 삭제하기로 결정 왜 .... 나를 넘어)
ragerdl

75

Valgrind와 Callgrind를 프로파일 링 도구 모음의 기반으로 사용합니다. 알아야 할 중요한 것은 Valgrind가 기본적으로 가상 머신이라는 것입니다.

(wikipedia) Valgrind는 본질적으로 동적 재 컴파일을 포함하여 JIT (Just-In-Time) 컴파일 기술을 사용하는 가상 머신입니다. 원래 프로그램의 어떤 것도 호스트 프로세서에서 직접 실행되지 않습니다. 대신 Valgrind는 먼저 프로그램을 중립적 표현 (IR)이라고하는 임시적이고 간단한 형식으로 변환합니다.이 형식은 프로세서 중립적 인 SSA 기반 형식입니다. 변환 후 Valgrind가 IR을 기계 코드로 다시 변환하고 호스트 프로세서가이를 실행하기 전에 도구 (아래 참조)가 IR에서 원하는 변환을 자유롭게 수행 할 수 있습니다.

Callgrind는 그 위에 구축 된 프로파일 러입니다. 주요 이점은 신뢰할 수있는 결과를 얻기 위해 몇 시간 동안 애플리케이션을 실행할 필요가 없다는 것입니다. Callgrind는 비 프로빙 프로파일 러 이기 때문에 단 1 초만으로도 견고하고 안정적인 결과를 얻을 수 있습니다.

Valgrind에 구축 된 또 다른 도구는 Massif입니다. 힙 메모리 사용량을 프로파일 링하는 데 사용합니다. 잘 작동합니다. 메모리 사용량에 대한 스냅 샷을 제공합니다. 자세한 정보 WHAT에는 몇 퍼센트의 메모리가 저장되어 있으며 WHO는이를 저장했습니다. 이러한 정보는 응용 프로그램 실행 시점에 따라 다릅니다.


70

실행 valgrind --tool=callgrind옵션은 일부 옵션이 없으면 완전하지 않습니다. 우리는 일반적으로 Valgrind에서 10 분의 느린 시작 시간을 프로파일 링하고 싶지 않으며 어떤 작업을 수행 할 때 프로그램을 프로파일 링하려고합니다.

이것이 제가 추천하는 것입니다. 먼저 프로그램을 실행하십시오.

valgrind --tool=callgrind --dump-instr=yes -v --instr-atstart=no ./binary > tmp

이제 작동하고 프로파일 링을 시작하려면 다른 창에서 실행해야합니다.

callgrind_control -i on

프로파일 링이 켜집니다. 전원을 끄고 전체 작업을 중지하려면 다음을 사용할 수 있습니다.

callgrind_control -k

이제 현재 디렉토리에 callgrind.out. *라는 파일이 있습니다. 프로파일 링 결과를 보려면 다음을 사용하십시오.

kcachegrind callgrind.out.*

다음 창에서 "Self"열 머리글을 클릭하는 것이 좋습니다. 그렇지 않으면 "main ()"이 가장 많은 시간이 걸리는 작업임을 나타냅니다. "자기"는 각 기능 자체가 종속 자와 함께가 아니라 시간이 얼마나 걸렸는지를 보여줍니다.


9
어떤 이유로 callgrind.out. * 파일은 항상 비어있었습니다. callgrind_control -d를 실행하면 데이터를 디스크로 강제 덤프 할 수있었습니다.
Tõnu Samuel

3
캔트. 내 일반적인 컨텍스트는 전체 MySQL 또는 PHP 또는 이와 유사한 큰 것입니다. 종종 처음에 무엇을 분리하고 싶어하는지조차 모릅니다.
Tõnu Samuel

2
또는 내 경우에는 내 프로그램이 실제로 많은 양의 데이터를 LRU 캐시에로드하며 프로파일 링하지 않으려 고합니다. 따라서 시작할 때 캐시의 하위 집합을 강제로로드하고 해당 데이터 만 사용하여 코드를 프로파일 링합니다 (OS + CPU가 캐시 내에서 메모리 사용을 관리하도록 함). 작동하지만 다른 캐시에서 프로파일 링하려고하는 코드에서 캐시를로드하는 속도가 느리고 CPU를 많이 사용하므로 callgrind는 오염 된 결과를 생성합니다.
Code Abominator

2
또한이 CALLGRIND_TOGGLE_COLLECT활성화 / 비활성화 수집 프로그램; stackoverflow.com/a/13700817/288875
Andre Holzner

1
와우, 나는 이것이 존재하는지 몰랐습니다, 감사합니다!
Vincent Fourmond 19

59

이것은 Nazgob의 Gprof 답변에 대한 답변 입니다.

지난 며칠 동안 Gprof를 사용해 왔으며 이미 세 가지 중요한 제한 사항을 발견했습니다. 그 중 하나는 아직 다른 곳에서는 문서화되지 않은 것입니다.

  1. 해결 방법 을 사용하지 않으면 멀티 스레드 코드에서 제대로 작동하지 않습니다.

  2. 함수 그래프에 의해 호출 그래프가 혼동됩니다. 예 : multithread()지정된 배열 (인수로 전달됨)에서 지정된 함수를 멀티 스레드 할 수 있는 함수 가 있습니다. 그러나 Gprof는 모든 통화 multithread()시간은 어린이의 시간을 계산할 목적으로 동등한 것으로 간주합니다. 일부 함수 multithread()는 다른 함수 보다 훨씬 오래 걸리기 때문에 호출 그래프는 대부분 쓸모가 없습니다. (스레딩이 문제인지 궁금한 사람들에게 : 아니오, multithread()선택적으로,이 경우에는 호출 스레드에서만 모든 것을 순차적으로 실행할 수 있습니다).

  3. 그것은 말한다 여기 ... "수 - 중 - 통화 수치는 샘플링, 계산에 의해하지 파생됩니다. 그들은 정확 ...이다"고. 그러나 내 호출 그래프는 5345859132 + 784984078을 가장 많이 호출되는 함수에 대한 호출 통계로 제공합니다. 첫 번째 숫자는 직접 호출이고 두 번째 재귀 호출은 모두 자체 호출입니다. 이것은 버그가 있음을 암시했기 때문에 긴 (64 비트) 카운터를 코드에 넣고 다시 실행했습니다. 내 수 : 5345859132 직통 및 78094395406 자체 재귀 호출. 여기에는 많은 자릿수가 있으므로 Gprof의 784m에 비해 내가 측정하는 재귀 호출은 780 억입니다 .100 가지 요소입니다. 두 실행 모두 단일 스레드 및 최적화되지 않은 코드로, 하나는 컴파일 -g되고 다른 하나는 실행되었습니다 -pg.

이것은 64 비트 데비안 Lenny에서 실행되는 GNU Gprof (GNU Binutils for Debian) 2.18.0.20080103입니다.


그렇습니다. 샘플링을 수행하지만 호출 횟수 수치는 아닙니다. 흥미롭게도 귀하의 링크를 따라 가면 내 게시물에 링크 된 매뉴얼 페이지의 업데이트 된 버전 인 새 URL 인 sourceware.org/binutils/docs/gprof/로 연결되었습니다. 이것은 내 답변의 (iii) 부분의 인용문을 반복합니다. "다중 스레드 응용 프로그램 또는 다중 스레드 라이브러리와 연결되는 단일 스레드 응용 프로그램에서 계산 기능이 스레드 안전 인 경우에만 계산이 결정적입니다. (참고 : glibc의 mcount 계산 기능은 스레드가 아닙니다. -안전한)."
Rob_before_edits 2016 년

이것이 (iii)에 내 결과를 설명하는지 확실하지 않습니다. 내 코드는 -lpthread -lm에 연결되었으며 단일 스레드를 실행할 때도 "pthread_t * thr"및 "pthread_mutex_t nextLock = PTHREAD_MUTEX_INITIALIZER"정적 변수를 선언했습니다. 나는 일반적으로 "멀티 스레드 라이브러리와의 링크"는 실제로 그 라이브러리를 사용한다는 것을 의미한다고 생각하지만, 이것보다 더 큰 범위에서 틀릴 수 있습니다!
Rob_before_edits

23

Valgrind, callgrind 및 kcachegrind를 사용하십시오.

valgrind --tool=callgrind ./(Your binary)

callgrind.out.x를 생성합니다. kcachegrind를 사용하여 읽으십시오.

gprof 사용 (-pg 추가) :

cc -o myprog myprog.c utils.c -g -pg 

(멀티 스레드, 함수 포인터에는 좋지 않습니다)

google-perftools를 사용하십시오.

시간 샘플링을 사용하여 I / O 및 CPU 병목 현상이 드러납니다.

Intel VTune이 최고입니다 (교육 목적으로 무료).

기타 : AMD Codeanalyst (AMD CodeXL로 대체 된 이후), OProfile, 'perf'도구 (apt-get install linux-tools)


10

C ++ 프로파일 링 기술 조사

이 답변에서는 여러 도구를 사용하여 몇 가지 매우 간단한 테스트 프로그램을 분석하여 해당 도구의 작동 방식을 구체적으로 비교합니다.

다음 테스트 프로그램은 매우 간단하며 다음을 수행합니다.

  • main통화 fastmaybe_slow3 회, maybe_slow통화 중 하나 가 느림

    의 느린 호출 maybe_slow은 10 배 더 길며 자식 함수 호출을 고려하면 런타임을 지배합니다 common. 이상적으로 프로파일 링 도구는 특정 느린 호출을 가리킬 수 있습니다.

  • 모두 fastmaybe_slow전화 common프로그램 실행의 대부분을 차지하고,

  • 프로그램 인터페이스는 다음과 같습니다

    ./main.out [n [seed]]

    프로그램은 O(n^2)총 루프를 수행합니다. seed런타임에 영향을 미치지 않고 다른 출력을 얻는 것입니다.

main.c

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t __attribute__ ((noinline)) common(uint64_t n, uint64_t seed) {
    for (uint64_t i = 0; i < n; ++i) {
        seed = (seed * seed) - (3 * seed) + 1;
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) fast(uint64_t n, uint64_t seed) {
    uint64_t max = (n / 10) + 1;
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) maybe_slow(uint64_t n, uint64_t seed, int is_slow) {
    uint64_t max = n;
    if (is_slow) {
        max *= 10;
    }
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

int main(int argc, char **argv) {
    uint64_t n, seed;
    if (argc > 1) {
        n = strtoll(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    if (argc > 2) {
        seed = strtoll(argv[2], NULL, 0);
    } else {
        seed = 0;
    }
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 1);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    printf("%" PRIX64 "\n", seed);
    return EXIT_SUCCESS;
}

gprof

gprof는 계측으로 소프트웨어를 다시 컴파일해야하며, 계측과 함께 샘플링 방식도 사용합니다. 따라서 정확도 (샘플링이 항상 정확하지는 않으며 기능을 건너 뛸 수 있음)와 실행 속도 저하 (계측 및 샘플링은 실행 속도를 크게 저하시키지 않는 비교적 빠른 기술) 사이의 균형을 유지합니다.

gprof는 GCC / binutils에 내장되어 있으므로 -pggprof를 활성화 하는 옵션으로 컴파일 하면됩니다. 그런 다음 몇 초 ( 10000) 의 적당한 기간 동안 실행되는 size CLI 매개 변수를 사용하여 프로그램을 정상적으로 실행합니다 .

gcc -pg -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time ./main.out 10000

교육상의 이유로 최적화를 사용하지 않고 실행도 수행합니다. 일반적으로 최적화 된 프로그램의 성능 최적화에만 관심이 있기 때문에 실제로는 쓸모가 없습니다.

gcc -pg -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 10000

첫째, time유무에 관계없이 실행 시간 -pg이 동일 하다는 것을 알 수 있습니다 . 그러나이 티켓에 나와있는 것처럼 복잡한 소프트웨어에서 2x-3x 속도 저하가 발생했습니다 .

로 컴파일 했으므로 -pg프로그램을 실행 gmon.out하면 프로파일 링 데이터가 포함 된 파일 파일이 생성 됩니다.

우리는 그래픽으로 해당 파일을 관찰 할 수 gprof2dot에서 질문과 같이 이 gprof은 결과의 그래픽 표현을 얻을 수 있습니까?

sudo apt install graphviz
python3 -m pip install --user gprof2dot
gprof main.out > main.gprof
gprof2dot < main.gprof | dot -Tsvg -o output.svg

여기서, gprof공구가 판독 gmon.out추적 정보와의 판독 가능한 보고서 생성 main.gprof, gprof2dot다음 그래프를 생성하기 위해 판독한다.

gprof2dot의 출처는 https://github.com/jrfonseca/gprof2dot입니다.

-O0실행에 대해 다음을 관찰합니다 .

여기에 이미지 설명을 입력하십시오

그리고 -O3실행을 위해 :

여기에 이미지 설명을 입력하십시오

-O0출력은 거의 자명하다. 예를 들어, 3 개의 maybe_slow호출과 해당 자식 호출이 총 런타임의 97.56 %를 차지하지만 maybe_slow자식없이 자체 실행 하는 것이 총 실행 시간의 0.00 %를 차지합니다. 즉, 해당 함수에 소비 된 거의 모든 시간이 어린이 전화.

TODO : 왜 GDB에서 출력을 볼 수 있는데 출력 main에서 누락 되었습니까? GProf 출력에서 ​​누락 된 기능 나는 gprof가 컴파일 된 계측 외에도 샘플링을 기반으로하고 있기 때문에 너무 빠르며 샘플 이 없기 때문이라고 생각합니다 .-O3bt-O3 main

SVG는 Ctrl + F로 검색 가능하고 파일 크기는 약 10 배 작을 수 있기 때문에 PNG 대신 SVG 출력을 선택합니다. 또한 생성 된 이미지의 너비와 높이는 복잡한 소프트웨어의 경우 수만 개의 픽셀로 eog가득 차 있으며 PNG의 경우 그놈 3.28.1 버그가 발생하지만 SVG는 브라우저에서 자동으로 열립니다. 김프 2.8도 잘 작동했습니다.

그러나 그때까지도 이미지를 많이 드래그하여 원하는 것을 찾을 수 있습니다. 예를 들어이 티켓 에서 가져온 "실제"소프트웨어 예의 이미지를 참조하십시오 .

여기에 이미지 설명을 입력하십시오

모든 분류되지 않은 작은 스파게티 라인이 서로 연결되어 가장 중요한 통화 스택을 쉽게 찾을 수 있습니까? 더 나은 dot옵션 이있을 수 있지만 지금은 가고 싶지 않습니다. 우리에게 정말로 필요한 것은 그것을위한 적절한 전용 뷰어이지만 아직 찾지 못했습니다.

그러나 색상 맵을 사용하여 이러한 문제를 약간 완화 할 수 있습니다. 예를 들어, 이전의 거대한 이미지에서 나는 녹색이 빨간색을 쫓은 화려한 공제를했을 때 마침내 왼쪽에서 중요한 경로를 찾았습니다.

또는 gprof이전에 저장 한 내장 binutils 도구 의 텍스트 출력을 관찰 할 수도 있습니다 .

cat main.gprof

기본적으로 출력 데이터의 의미를 설명하는 매우 자세한 출력을 생성합니다. 그보다 더 잘 설명 할 수 없기 때문에 직접 읽어 보도록하겠습니다.

데이터 출력 형식을 이해 한 후에는 -b옵션 없이 튜토리얼없이 데이터 만 표시하도록 상세도를 줄일 수 있습니다 .

gprof -b main.out

이 예에서 출력은 다음과 -O0같습니다.

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
100.35      3.67     3.67   123003     0.00     0.00  common
  0.00      3.67     0.00        3     0.00     0.03  fast
  0.00      3.67     0.00        3     0.00     1.19  maybe_slow

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.27% of 3.67 seconds

index % time    self  children    called     name
                0.09    0.00    3003/123003      fast [4]
                3.58    0.00  120000/123003      maybe_slow [3]
[1]    100.0    3.67    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    3.67                 main [2]
                0.00    3.58       3/3           maybe_slow [3]
                0.00    0.09       3/3           fast [4]
-----------------------------------------------
                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]
-----------------------------------------------
                0.00    0.09       3/3           main [2]
[4]      2.4    0.00    0.09       3         fast [4]
                0.09    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common                  [4] fast                    [3] maybe_slow

그리고 -O3:

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
100.52      1.84     1.84   123003    14.96    14.96  common

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.54% of 1.84 seconds

index % time    self  children    called     name
                0.04    0.00    3003/123003      fast [3]
                1.79    0.00  120000/123003      maybe_slow [2]
[1]    100.0    1.84    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]     97.6    0.00    1.79                 maybe_slow [2]
                1.79    0.00  120000/123003      common [1]
-----------------------------------------------
                                                 <spontaneous>
[3]      2.4    0.00    0.04                 fast [3]
                0.04    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common

각 섹션에 대한 매우 빠른 요약 예 :

                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]

들여 쓰기 된 기능을 중심으로합니다 ( maybe_flow). [3]해당 함수의 ID입니다. 함수 위에는 호출자가 있고 그 아래에는 호출자가 있습니다.

에 대해서는 -O3그래픽 출력에서 maybe_slowfast같이 알려진 부모가없는 문서를 참조하십시오 <spontaneous>.

gprof로 라인 별 프로파일 링을 수행하는 좋은 방법이 있는지 확실하지 않습니다. 특정 코드 라인에서 보낸 ' gprof '시간

valgrind callgrind

valgrind는 valgrind 가상 머신을 통해 프로그램을 실행합니다. 이렇게하면 프로파일 링이 매우 정확 해지지 만 프로그램 속도가 크게 느려집니다. 나는 또한 kcachegrind에 대해 언급했다 : 코드의 그림 함수 호출 그래프를 얻는 도구

callgrind는 코드를 프로파일 링하는 valgrind의 도구이고 kcachegrind는 cachegrind 출력을 시각화 할 수있는 KDE 프로그램입니다.

먼저 -pg일반 컴파일로 돌아가려면 플래그 를 제거해야합니다 . 그렇지 않으면 실행이 실제로 실패 Profiling timer expired하고 그렇습니다. 그렇습니다.

따라서 다음과 같이 컴파일하고 실행합니다.

sudo apt install kcachegrind valgrind
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time valgrind --tool=callgrind valgrind --dump-instr=yes \
  --collect-jumps=yes ./main.out 10000

--dump-instr=yes --collect-jumps=yes이것은 또한 추가 된 오버 헤드 비용으로 어셈블리 라인 당 성능 저하를 볼 수있는 정보를 덤프하기 때문에 가능 합니다.

박쥐 time가 프로그램을 실행하는 데 29.5 초가 걸렸다 고 알려주므로이 예제에서는 약 15 배의 속도가 느려졌습니다. 분명히이 둔화는 더 큰 워크로드에 심각한 제한이 될 것입니다. 여기언급 된 "실제 소프트웨어 예" 에서 80 배의 속도 저하를 관찰했습니다.

런라는 이름의 프로파일 데이터 파일 생성 callgrind.out.<pid>예를 들어 callgrind.out.8554내 경우를. 우리는 그 파일을 다음과 같이 봅니다 :

kcachegrind callgrind.out.8554

텍스트 gprof 출력과 유사한 데이터를 포함하는 GUI를 보여줍니다.

여기에 이미지 설명을 입력하십시오

또한 오른쪽 하단의 "콜 그래프"탭으로 이동하면 마우스 오른쪽 버튼을 클릭하여 내보낼 수있는 콜 그래프를 볼 수 있습니다.

여기에 이미지 설명을 입력하십시오

fastkcachegrind가 호출에 너무 적은 시간이 걸리기 때문에 시각화를 단순화해야했기 때문에 해당 그래프에 표시되지 않는다고 생각 합니다. 실제 프로그램에서 원하는 동작 일 것입니다. 마우스 오른쪽 버튼 메뉴에는 이러한 노드를 컬링하는시기를 제어하는 ​​몇 가지 설정이 있지만 빠른 시도 후 짧은 호출을 표시하지 못했습니다. fast왼쪽 창을 클릭 하면와 함께 호출 그래프가 표시되어 fast실제로 스택이 캡처되었습니다. 아직 완전한 그래프 호출 그래프를 표시하는 방법을 찾지 못했습니다 : callgrind가 kcachegrind 호출 그래프에서 모든 함수 호출을 표시하도록하십시오.

복잡한 C ++ 소프트웨어의 TODO에서 유형의 일부 항목을 볼 수 있습니다 <cycle N>. 예를 들어 <cycle 11>함수 이름이 필요한 위치는 무엇입니까? "Cycle Detection (사이클 감지)"버튼을 사용하여이 기능을 켜고 끌 수 있지만 그 의미는 무엇입니까?

perf ...에서 linux-tools

perf독점적으로 Linux 커널 샘플링 메커니즘을 사용하는 것 같습니다. 따라서 설정이 매우 간단하지만 완전히 정확하지는 않습니다.

sudo apt install linux-tools
time perf record -g ./main.out 10000

이것은 실행에 0.2 초를 추가 했으므로 시간이 좋지만 common키보드 오른쪽 화살표로 노드를 확장 한 후에도 여전히 관심이 없습니다 .

Samples: 7K of event 'cycles:uppp', Event count (approx.): 6228527608     
  Children      Self  Command   Shared Object     Symbol                  
-   99.98%    99.88%  main.out  main.out          [.] common              
     common                                                               
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.01%     0.01%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.01%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.01%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.01%     0.00%  main.out  ld-2.27.so        [.] mprotect            
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.01%     0.00%  main.out  ld-2.27.so        [.] _xstat              
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x2f3d4f4944555453  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007fff3cfc57ac  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

그런 다음 -O0프로그램 을 벤치마킹하여 그것이 무엇인가를 나타내는 지 확인 하려고합니다 . 마지막으로 콜 그래프가 표시됩니까?

Samples: 15K of event 'cycles:uppp', Event count (approx.): 12438962281   
  Children      Self  Command   Shared Object     Symbol                  
+   99.99%     0.00%  main.out  [unknown]         [.] 0x04be258d4c544155  
+   99.99%     0.00%  main.out  libc-2.27.so      [.] __libc_start_main   
-   99.99%     0.00%  main.out  main.out          [.] main                
   - main                                                                 
      - 97.54% maybe_slow                                                 
           common                                                         
      - 2.45% fast                                                        
           common                                                         
+   99.96%    99.85%  main.out  main.out          [.] common              
+   97.54%     0.03%  main.out  main.out          [.] maybe_slow          
+    2.45%     0.00%  main.out  main.out          [.] fast                
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.00%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.00%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_lookup_symbol_x 
     0.00%     0.00%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.00%     0.00%  main.out  ld-2.27.so        [.] mmap64              
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x552e53555f6e653d  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007ffe1cf20fdb  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

TODO : -O3처형은 어떻게 되었습니까? 그것은 단순히인가 maybe_slow하고 fast너무 빨리했다 및 샘플을하지 않았다? 실행 -O3하는 데 시간이 오래 걸리는 더 큰 프로그램에서 잘 작동합니까 ? CLI 옵션이 누락 되었습니까? 나는에 대해 알게 -F헤르츠의 샘플 주파수를 제어하는,하지만 난의 기본적으로 허용 최대로 켜져 -F 39500(증가 될 수있다 sudo)와 나는 아직도 선명한 통화가 표시되지 않습니다.

멋진 점 중 하나 perf는 Brendan Gregg의 FlameGraph 도구입니다.이 도구는 통화 스택 타이밍을 매우 깔끔하게 표시하여 큰 통화를 빠르게 볼 수 있도록합니다. 이 도구는에서 확인할 수 있습니다 https://github.com/brendangregg/FlameGraph 또한 그의 반환 한 튜토리얼에 언급 : http://www.brendangregg.com/perf.html#FlameGraphs 내가 달릴 때 perf없이 sudo내가 가진 ERROR: No stack counts found너무에 대한 이제 나는 그것을 할 것이다 sudo:

git clone https://github.com/brendangregg/FlameGraph
sudo perf record -F 99 -g -o perf_with_stack.data ./main.out 10000
sudo perf script -i perf_with_stack.data | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

그러나 그러한 간단한 프로그램에서는 출력을 이해하기가 쉽지 maybe_slow않습니다 fast. 그래프에서 그래프 도 볼 수 없기 때문입니다.

여기에 이미지 설명을 입력하십시오

보다 복잡한 예에서는 그래프의 의미가 분명해집니다.

여기에 이미지 설명을 입력하십시오

TODO이 [unknown]예제 에는 함수 로그가 있는데 왜 그럴까요?

다른 perf GUI 인터페이스에는 다음이 포함됩니다.

  • Eclipse Trace Compass 플러그인 : https://www.eclipse.org/tracecompass/

    그러나 이것은 먼저 데이터를 Common Trace Format으로 변환해야한다는 단점이 perf data --to-ctf있지만 빌드 타임에 활성화해야 perf하거나 충분히 새로 워야합니다. 우분투 18.04

  • https://github.com/KDAB/hotspot

    이것의 단점은 우분투 패키지가없는 것 같으며 Qt 5.10이 필요하지만 Ubuntu 18.04는 Qt 5.9입니다.

gperftools

이전에 "Google 성능 도구"라고하는 출처 : https://github.com/gperftools/gperftools 샘플 기반.

먼저 다음을 사용하여 gperftools를 설치하십시오.

sudo apt install google-perftools

그런 다음 gperftools CPU 프로파일 러를 런타임 또는 빌드 타임의 두 가지 방법으로 활성화 할 수 있습니다.

런타임시, 우리는 설정 통과해야 LD_PRELOAD가리 키도록 libprofiler.so당신이 찾을 수있는 locate libprofiler.so, 예를 들어 내 시스템 :

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libprofiler.so \
  CPUPROFILE=prof.out ./main.out 10000

또는 링크 타임에 라이브러리를 빌드하여 LD_PRELOAD런타임에 전달 하지 않아도됩니다.

gcc -Wl,--no-as-needed,-lprofiler,--as-needed -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
CPUPROFILE=prof.out ./main.out 10000

참조 : gperftools -하지 덤프 프로필 파일

내가 지금까지 찾은이 데이터를 보는 가장 좋은 방법은 pprof 출력을 kcachegrind가 입력 (예 : Valgrind-project-viewer-tool)과 동일한 형식으로 만들고 kcachegrind를 사용하여 다음을 확인하는 것입니다.

google-pprof --callgrind main.out prof.out  > callgrind.out
kcachegrind callgrind.out

이러한 방법 중 하나를 사용하여 실행 한 후 prof.out프로파일 데이터 파일을 출력으로 얻습니다 . 다음을 사용하여 해당 파일을 그래픽으로 SVG로 볼 수 있습니다.

google-pprof --web main.out prof.out

여기에 이미지 설명을 입력하십시오

다른 도구와 마찬가지로 친숙한 콜 그래프를 제공하지만 초 단위가 아닌 수 많은 샘플 단위로 구성됩니다.

또는 다음과 같은 텍스트 데이터를 얻을 수도 있습니다.

google-pprof --text main.out prof.out

이것은 다음을 제공합니다.

Using local file main.out.
Using local file prof.out.
Total: 187 samples
     187 100.0% 100.0%      187 100.0% common
       0   0.0% 100.0%      187 100.0% __libc_start_main
       0   0.0% 100.0%      187 100.0% _start
       0   0.0% 100.0%        4   2.1% fast
       0   0.0% 100.0%      187 100.0% main
       0   0.0% 100.0%      183  97.9% maybe_slow

도 참조 : 어떻게 도구를 규칙적 구글 사용

우분투 18.04, gprof2dot 2019.11.30, valgrind 3.13.0, perf 4.15.18, Linux 커널 4.15.0, FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b, gperftools 2.5-2에서 테스트되었습니다.


2
기본적으로 perf 레코드는 프레임 포인터 레지스터를 사용합니다. 최신 컴파일러는 프레임 주소를 기록하지 않고 대신 레지스터를 범용으로 사용합니다. 대안은 -fno-omit-frame-pointer플래그 로 컴파일 하거나 시나리오 와 함께 --call-graph "dwarf"또는 --call-graph "lbr"시나리오에 따라 다른 대안을 사용하는 것 입니다.
Jorge Bellon

5

단일 스레드 프로그램의 경우 igprof , Ignominous Profiler : https://igprof.org/를 사용할 수 있습니다 .

Mike Dunlavey의 답변에 따르면 ... 길이를 따라 샘플링 프로파일 러입니다.이 결과는 누적 또는 기능별.


흥미롭게 보이지만 GCC 9.2로 컴파일하지 못했습니다. (Debian / Sid) github에 문제가 있습니다.
Basile Starynkevitch

5

또한 언급 할 가치가 있습니다

  1. HPCToolkit ( http://hpctoolkit.org/)- 오픈 소스, 병렬 프로그램에서 작동하고 여러 가지 방법으로 결과를 볼 수있는 GUI
  2. Intel VTune ( https://software.intel.com/en-us/vtune)- 인텔 컴파일러가있는 경우 매우 좋습니다
  3. TAU ( http://www.cs.uoregon.edu/research/tau/home.php )

HPCToolkit 및 VTune을 사용했으며 텐트에서 장대를 찾는 데 매우 효과적이며 코드를 다시 컴파일 할 필요가 없습니다 (의미있는 출력을 얻으려면 CMake에서 -g -O 또는 RelWithDebInfo 유형 빌드를 사용해야 함) . TAU의 기능이 비슷하다고 들었습니다.


4

다음은 코드 속도를 높이기 위해 사용하는 두 가지 방법입니다.

CPU 바운드 응용 프로그램의 경우 :

  1. 디버그 모드에서 프로파일 러를 사용하여 코드의 의심스러운 부분을 식별하십시오.
  2. 그런 다음 RELEASE 모드로 전환하고 성능의 변화가 나타날 때까지 코드의 문제가있는 부분을 주석 처리하십시오 (아무것도 스텁하지 않음).

I / O 바운드 애플리케이션의 경우 :

  1. RELEASE 모드에서 프로파일 러를 사용하여 코드의 의심스러운 부분을 식별하십시오.

NB

프로파일 러가없는 경우 가난한 사람의 프로파일 러를 사용하십시오. 응용 프로그램을 디버깅하는 동안 일시 중지를 누르십시오. 대부분의 개발자 제품군은 주석이 달린 줄 번호로 조립됩니다. 통계적으로 대부분의 CPU 사이클을 먹는 지역에 착륙 할 가능성이 있습니다.

CPU의 경우 DEBUG 모드 에서 프로파일 링하는 이유 는 RELEASE 모드 에서 프로파일 링을 시도한 경우 컴파일러가 계산할 때 코드를 매핑 할 수없는 엉망으로 만드는 경향이있는 수학, 루프 및 인라인 함수를 줄이려고하기 때문입니다. 매핑 할 수없는 엉망은 어셈블리가 최적화중인 소스 코드와 일치하지 않을 수 있기 때문에 프로파일 러가 너무 오래 걸린 것을 명확하게 식별 할 수 없음을 의미합니다 . RELEASE 모드 의 성능 (예 : 타이밍 감지)이 필요한 경우 사용 가능한 성능을 유지하는 데 필요한 디버거 기능을 비활성화하십시오.

I를 들어 / O 바인딩은 프로파일 러는 여전히 / O 작업을 I을 식별 할 수 RELEASE의 내가 / O 작업이 중 외부 공유 라이브러리에 연결되어 있습니다 (대부분의 시간) 또는 최악의 경우, sys- 발생하기 때문 모드 호출 인터럽트 벡터 (프로파일 러로 쉽게 식별 할 수 있음).


2
+1 가난한 사람의 방법은 CPU 바운드와 마찬가지로 I / O 바운드에서도 작동하며 DEBUG 모드에서 모든 성능 조정을 수행하는 것이 좋습니다. 튜닝이 끝나면 RELEASE를 켜십시오. 프로그램이 코드에서 CPU에 바인딩되어 있으면 개선됩니다. 다음은 그 과정에 대한 조잡하지만 짧은 비디오입니다.
Mike Dunlavey 2016 년

3
성능 프로파일 링에는 DEBUG 빌드를 사용하지 않습니다. DEBUG 모드의 성능 중요 부품이 릴리스 모드에서 완전히 최적화되는 경우가 종종 있습니다. 또 다른 문제는 디버그 코드에서 어설 션을 사용하여 성능에 노이즈를 추가하는 것입니다.
gast128

3
내 게시물을 읽었습니까? "RELEASE 모드의 성능 (예 : 타이밍에 민감한)이 필요한 경우 사용 가능한 성능을 유지하는 데 필요한 디버거 기능을 비활성화하십시오." 성능의 변화. "? 언급 한 함정을 피하기 위해 디버그 모드에서 가능한 문제 영역을 확인하고 릴리스 모드에서 해당 문제를 확인한다고 말했습니다.
seo


2

loguru프로파일 링에 훌륭하게 사용할 수있는 타임 스탬프 및 총 가동 시간이 포함되어 있으므로 로깅 프레임 워크 를 사용할 수 있습니다.


1

직장에서 우리는 스케줄링 측면에서 원하는 것을 모니터링하는 데 도움이되는 멋진 도구를 가지고 있습니다. 이것은 여러 번 유용했습니다.

C ++로되어 있으며 필요에 따라 사용자 정의해야합니다. 불행히도 코드를 공유 할 수없고 개념 만 공유 할 수 있습니다. volatile포스트 모템을 덤프하거나 로깅 시스템을 중지 한 후 (예를 들어 파일에 덤프) 타임 스탬프 및 이벤트 ID가 포함 된 "큰" 버퍼를 사용합니다 .

모든 데이터가 포함 된 이른바 큰 버퍼를 검색하고 작은 인터페이스가이를 구문 분석하고 오실로스코프가 색상 ( .hpp파일로 구성)과 같이 이름 (위 / 아래 + 값)으로 이벤트를 표시합니다 .

원하는 것에 만 집중하도록 생성 된 이벤트 양을 사용자 정의합니다. 초당 기록 된 이벤트 수에 따라 원하는 CPU 양을 사용하면서 문제를 예약하는 데 많은 도움이되었습니다.

3 개의 파일이 필요합니다.

toolname.hpp // interface
toolname.cpp // code
tool_events_id.hpp // Events ID

개념은 다음 tool_events_id.hpp과 같이 이벤트를 정의하는 것입니다.

// EVENT_NAME                         ID      BEGIN_END BG_COLOR NAME
#define SOCK_PDU_RECV_D               0x0301  //@D00301 BGEEAAAA # TX_PDU_Recv
#define SOCK_PDU_RECV_F               0x0302  //@F00301 BGEEAAAA # TX_PDU_Recv

또한 다음에서 몇 가지 기능을 정의합니다 toolname.hpp.

#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
// ...

void init(void);
void probe(id,payload);
// etc

코드 어디에서나 사용할 수 있습니다 :

toolname<LOG_LEVEL>::log(EVENT_NAME,VALUE);

probe함수는 몇 개의 어셈블리 라인을 사용하여 클럭 타임 스탬프를 최대한 빨리 검색 한 다음 버퍼에 항목을 설정합니다. 또한 로그 이벤트를 저장할 인덱스를 안전하게 찾기 위해 원자 단위로 증가합니다. 물론 버퍼는 원형입니다.

샘플 코드가 부족하여 아이디어가 난독 화되지 않기를 바랍니다.


1

실제로 google / benchmark 에 대해 언급하지 않은 많은 사람들이 놀랐습니다 . 특히 코드베이스가 조금 큰 경우 특정 코드 영역을 고정하는 것이 약간 번거롭지 만,callgrind

병목 현상을 일으키는 부분을 식별하는 IMHO가 핵심입니다. 그러나 먼저 다음 질문에 답하고 그에 따라 도구를 선택합니다.

  1. 내 알고리즘이 맞습니까?
  2. 병목으로 증명 된 자물쇠가 있습니까?
  3. 범인으로 입증되는 특정 코드 섹션이 있습니까?
  4. 처리, 최적화 된 IO는 어떻습니까?

valgrind의 조합 callrindkcachegrind위의 점에서 괜찮은 평가를 제공해야하고이 코드의 일부 섹션에 문제가 있다는 것을 설립 된 후에는, 내가 마이크로 벤치 마크를 수행 좋을 것은 google benchmark시작하기에 좋은 장소입니다.


1

-pg코드를 컴파일하고 링크 할 때 플래그를 사용 하고 실행 파일을 실행하십시오. 이 프로그램이 실행되는 동안 프로파일 링 데이터는 파일 a.out에 수집됩니다.
프로파일 링에는 두 가지 유형이 있습니다

1-플랫 프로파일 링 :
명령 gprog --flat-profile a.out을 실행하여
함수에 소요 된 전체 시간의 백분율, 함수에 소요 된 시간 (
하위 함수에 대한 호출 포함 및 제외)
- 통화
- 통화 당 평균 시간입니다.

2- 그래프
는 명령 gprof --graph a.out을 프로파일 링 하여 다음을 포함하는 각 기능에 대한 다음 데이터를 얻습니다
.-각 섹션에서 하나의 기능은 색인 번호로 표시됩니다.
-위의 함수에는 함수를 호출하는 함수 목록이 있습니다.
-함수 아래에 함수가 호출하는 함수 목록이 있습니다.

자세한 정보는 https://sourceware.org/binutils/docs-2.32/gprof/를 참조하십시오.


0

아무도 Arm MAP을 언급하지 않았으므로 개인적으로 Map을 사용하여 C ++ 과학 프로그램을 프로파일 링하는 데 성공적으로 추가했습니다.

Arm MAP은 병렬, 다중 스레드 또는 단일 스레드 C, C ++, 포트란 및 F90 코드 용 프로파일 러입니다. 소스 라인에 대한 심층 분석 및 병목 현상을 정확하게 제공합니다. 대부분의 프로파일 러와 달리 pthreads, OpenMP 또는 MPI를 병렬 및 스레드 코드로 프로파일 링 할 수 있도록 설계되었습니다.

MAP는 상용 소프트웨어입니다.


0

디버깅 소프트웨어 를 사용하여 코드가 느리게 실행되는 위치를 식별하는 방법은 무엇입니까?

운동 중에 장애물이 있다고 생각하면 속도가 느려집니다.

원치 않는 재 할당의 루핑, 버퍼 오버플로, 검색, 메모리 누수 등의 작업은 더 많은 실행 전력을 소비하므로 코드 성능에 부정적인 영향을 미칩니다. 프로파일 링 전에 컴파일에 -pg를 추가하십시오.

g++ your_prg.cpp -pg또는 cc my_program.cpp -g -pg컴파일러에 따라

아직 시도하지 않았지만 google-perftools에 대한 좋은 소식을 들었습니다. 시도해 볼 가치가 있습니다.

valgrind --tool=callgrind ./(Your binary)

gmon.out 또는 callgrind.out.x라는 파일을 생성합니다. 그런 다음 kcachegrind 또는 디버거 도구를 사용하여이 파일을 읽을 수 있습니다. 어떤 라인에 얼마의 비용이 드는지와 같은 결과로 사물의 그래픽 분석을 제공합니다.

나도 그렇게 생각해

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.