다른 프로그램은 gprof와 같은 기능을 수행합니까?
답변
Valgrind 에는 KCacheGrind 라는 매우 훌륭한 시각화 도구가 포함 된 명령어 수 프로파일 러가 있습니다. Mike Dunlavey가 권장 한 것처럼 Valgrind는 스택에 프로 시저가 존재하는 명령의 일부를 계산하지만 상호 재귀가있는 경우 혼란스러워하는 것은 유감입니다. 그러나 비주얼 라이저는 매우 훌륭하고 수년 앞서 gprof
있습니다.
답변
역사적인 이유로 gprof (논문 읽기) 가 존재합니다. 성능 문제를 찾는 데 도움이 될 것이라고 생각되면 결코 광고되지 않았습니다. 논문의 내용은 다음과 같습니다.
이 프로파일을 사용하여 다양한 구현 비용을 비교하고 평가할 수 있습니다.
할 수 있습니다 말하지 않는다 식별 는 않지만, 평가 될 수있는 다양한 구현을 의미하는 특별한 상황에서, 그것은 수 :
특히 프로그램의 작은 부분이 실행 시간을 지배하는 것으로 밝혀진 경우.
현지화되지 않은 문제는 어떻습니까? 중요하지 않습니까? 결코 주장 하지 않은 gprof에 대한 기대를 두지 마십시오 . 그것은입니다 만 만 CPU 바인딩 작업, 측정 도구.
대신 사용해보십시오.
다음은 44 배 속도 향상의 예입니다.
속도는 730 배입니다.
다음은 8 분 비디오 데모입니다.
통계에 대한 설명은 다음과 같습니다.
비평에 대한 답변입니다.
프로그램에 대한 간단한 관찰이 있습니다. 주어진 실행에서 모든 명령은 전체 시간의 일부 (특히 call
명령)에 책임 이 있으며, 그렇지 않은 경우 시간이 소비되지 않습니다. 이 시간 동안 명령은 스택 **에 있습니다. 그것이 이해되면, 당신은 그것을 볼 수 있습니다-
gprof 는 다음과 같은 성능에 대한 신화를 구현합니다.
-
해당 프로그램 카운터 샘플링이 유용합니다.
기포 종류의 스칼라 값 배열과 같은 불필요한 핫스팟 병목 현상이있는 경우에만 유용합니다. 예를 들어, 문자열 비교를 사용하여 정렬로 변경하면 여전히 병목 현상이 발생하지만 핫스팟이 문자열 비교이므로 프로그램 카운터 샘플링에서이를 볼 수 없습니다. 반면에 확장 프로그램 카운터 (호출 스택) 를 샘플링 하는 경우 문자열 비교가 호출되는 지점 인 정렬 루프가 명확하게 표시됩니다. 실제로 gprof 는 pc 전용 샘플링의 한계를 해결하려는 시도였습니다. -
시간이 많이 걸리는 코드 라인을 캡처하는 것보다 타이밍 기능이 더 중요합니다.
그 신화의 이유는 gprof 가 스택 샘플을 캡처 할 수 없었기 때문에 대신 함수 시간을 지정하고 호출을 계산하며 호출 그래프를 캡처하려고 시도하기 때문입니다. 그러나 비용이 많이 드는 기능이 식별 된 후에도 여전히 해당 기능을 담당하는 회선을 찾아야합니다. 스택 샘플이있는 경우 살펴볼 필요가없는 경우 해당 라인은 샘플에 있습니다. 일반적인 함수에는 100-1000 개의 명령어가있을 수 있습니다. 함수 호출 은 1 개의 명령어이므로 비용이 많이 드는 호출을 찾는 것은 2-3 배 더 정확합니다. -
통화 그래프가 중요합니다.
프로그램에 대해 알아야 할 것은 시간을 소비하는 곳 이 아니라 왜. 함수에서 시간을 보낼 때 스택의 모든 코드 줄은 이유에 대한 추론 체인에서 하나의 링크를 제공합니다. 스택의 일부만 볼 수 있다면 이유의 일부만 볼 수 있으므로 해당 시간이 실제로 필요한지 확실하게 알 수 없습니다. 콜 그래프는 무엇을 알려줍니까? 각 호는 일부 함수 A가 일부 시간 동안 일부 함수 B를 호출하는 중임을 나타냅니다. A에 B를 호출하는 코드 줄이 하나만 있더라도 그 줄은 이유의 작은 부분만을 제공합니다. 당신이 충분히 운이 좋으면 아마도 그 라인에 나쁜 이유가있을 것입니다. 일반적으로 여러 개의 동시 회선을 확인하여 잘못된 이유가있는 경우이를 찾아야합니다. A가 둘 이상의 장소에서 B를 호출하면 덜 알려줍니다. -
그 재귀는 까다로운 혼란스러운 문제입니다. gprof 및 기타 프로파일 러는 호출 그래프를 생성 한 다음 시간을 노드에 부여해야한다는 필요성을 인식
하기 때문 입니다. 스택의 샘플이있는 경우 샘플에 나타나는 각 코드 줄의 시간 비용은 샘플의 일부인 매우 간단한 숫자입니다. 재귀가 있으면 주어진 줄이 샘플에 두 번 이상 나타날 수 있습니다.
문제 없어. Nms마다 샘플을 채취하고 그 라인이 F %에 표시된다고 가정합니다. 해당 라인을 삭제하거나 주변으로 분기하는 등의 시간이 걸리지 않으면 해당 샘플이 사라지고 시간이 F % 감소합니다. -
시간 측정 정확도 (따라서 많은 수의 샘플)가 중요합니다.
잠깐 생각 해봐 한 줄의 코드가 5 개 중 3 개의 샘플에 있다면, 전구처럼 쏠 수 있다면 사용 시간이 약 60 % 단축됩니다. 이제 5 개의 다른 샘플을 채취 한 경우 2 회 또는 4 회만 보았을 것입니다. 따라서 60 % 측정은 40 %에서 80 %의 일반적인 범위와 비슷합니다. 40 %에 불과한 경우 문제를 해결할 가치가 없다고 말할 수 있습니까? 그래서 당신이 정말로 원하는 것이 문제 를 찾는 것이라면 시간의 정확성은 무엇 입니까? 500 또는 5000 개의 샘플이 문제를 더 정밀하게 측정했지만 더 정확하게 발견하지는 못했습니다. -
명령문 또는 함수 호출 수를 계산하는 것이 유용합니다.
함수가 1000 번 호출되었다고 가정 해 봅시다. 비용의 몇 분의 시간을 알 수 있습니까? 또한 평균 실행 시간과 횟수를 곱한 후 총 시간으로 나누는 데 걸리는 시간을 알아야합니다. 평균 호출 시간은 나노초에서 초까지 다양 할 수 있으므로 카운트만으로는 많은 것을 알 수 없습니다. 스택 샘플이있는 경우 루틴 또는 명령문의 비용은 해당 샘플의 일부에 불과합니다. 루틴 또는 명령문이 시간을 소비하지 않으면 원칙적으로 전체 시간을 절약 할 수 있으므로 성능과 가장 직접적인 관계가있는 것입니다. -
차단 될 때 샘플을 채취 할 필요가 없음
이 신화의 이유는 1) 프로그램이 대기 중일 때 PC 샘플링이 의미가 없다는 것과 2) 타이밍 정확도가 높은 사전 점유입니다. 그러나 (1)의 경우 프로그램은 파일 I / O, 알아야 할 파일 및 어떤 스택 샘플이 표시 하는지 와 같이 요청한 내용을 매우 잘 기다리고있을 수 있습니다 . (물론 사용자 입력을 기다리는 동안 샘플을 제외하려고합니다.) (2) 프로그램이 다른 프로세스와의 경쟁 때문에 단순히 대기중인 경우, 실행되는 동안 상당히 임의의 방식으로 발생합니다. 따라서 프로그램이 오래 걸리더라도 통계에 큰 영향을 미치지는 않지만 명령문이 스택에있는 시간의 백분율입니다. -
“자체 시간”이 중요합니다.
셀프 시간은 라인 레벨이 아닌 기능 레벨에서 측정하는 경우에만 의미가 있으며, 함수 시간이 순전히 로컬 계산과 호출 된 루틴 사이에 있는지 판단하는 데 도움이 필요하다고 생각합니다. 라인 레벨에서 요약 할 경우, 라인은 스택의 끝에있는 경우 자체 시간을 나타내며, 그렇지 않으면 포함 시간을 나타냅니다. 어느 쪽이든, 비용은 스택 샘플의 백분율이므로 어느 쪽이든 찾을 수 있습니다. -
샘플을 고주파수로 가져와야합니다.
이것은 성능 문제가 빠르게 작용할 수 있고, 샘플을 자주 치기 위해서는 샘플이 자주 있어야한다는 생각에서 비롯됩니다. 그러나 문제가 총 실행 시간 10 초 (또는 그 밖의 시간) 중 20 %를 차지하는 경우 문제가 발생하더라도 해당 총 시간의 각 샘플은 20 %의 확률로 적중 할 수 있습니다. 이와 같은 단일 조각
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 샘플, 4 적중)
또는 이와 같은 많은 작은 조각
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 샘플, 3 적중)
또는 적중 횟수는 샘플 수에 관계없이 평균 약 5 분의 1입니다. 얼마나 적은. (평균 = 20 * 0.2 = 4. 표준 편차 = +/- sqrt (20 * 0.2 * 0.8) = 1.8.) -
병목 현상
이 하나만있는 것처럼 찾으려고 합니다 . 다음과 같은 실행 타임 라인을 고려하십시오 .로vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
표시되는 유용한 작업으로 구성됩니다.
. 각각vWxYz
1/2, 1/4, 1/8, 1/16, 1/32의 성능 문제 가 있습니다. 샘플링은v
쉽게 찾을 수 있습니다. 프로그램이 제거 된 후 제거됩니다.
xWzWxWYWxW.WxWYW
이제 프로그램을 실행하는W
데 절반의 시간이 걸리고 이제 절반의 시간이 걸리며 쉽게 찾을 수 있습니다. 제거, 남겨두기
xzxYx.xY
제거 할 항목을 찾을 수 없을 때까지 성능 문제를 백분율별로 가장 크게 제거 할 때마다이 프로세스가 계속됩니다. 이제 실행되는 유일한 것은.
원래 프로그램에서 사용한 시간의 1/32로 실행됩니다. 이것이 확대 효과입니다분모가 줄어들 기 때문에 문제를 제거하면 나머지가 백분율로 더 커집니다.
또 하나의 중요한 점은 모든 단일 문제를 찾아야한다는 것 입니다. 5 중 하나도 없습니다. 발견되지 않고 수정 된 문제는 최종 속도 향상 비율을 크게 줄입니다. 전부는 아니지만 일부를 찾는 것만으로는 충분하지 않습니다.
추가 : 나는 gprof 가 인기있는 이유 중 하나를 지적하고 싶습니다 -아마도 무료이며 가르치기 쉽고 오랫동안 사용되어 왔기 때문에 아마도 가르치고 있습니다. 빠른 Google 검색은 다음과 같은 교육 기관을 찾습니다 (또는 보이는 것처럼 보입니다).
버클리 부 clemson 콜로라도 듀크 백작 fsu 인디애나 mit msu ncsa.illinois ncsu nyu ou princeton ps stanford ucsd umd umich utah utexas utk wustl
** 작업 수행을 요청하는 다른 방법을 제외 하고 메시지 게시와 같은 이유를 알려주는 흔적을 남기지 않습니다 .
답변
perf
Linux에서 커널 및 사용자 응용 프로그램을 프로파일 링하는 비교적 새로운 도구에 대한 내용은 여기에서 보지 않았 으므로이 정보를 추가하기로 결정했습니다.
우선-이것은 리눅스 프로파일 링 에 대한 튜토리얼 입니다perf
perf
Linux 커널이 2.6.32보다 크거나 oprofile
오래된 경우 사용할 수 있습니다 . 두 프로그램 모두 프로그램을 인스트루먼트 할 필요가 없습니다 (예 : gprof
필요). 그러나 콜 그래프를 올바르게 얻으려면로 perf
프로그램을 작성해야합니다 -fno-omit-frame-pointer
. 예를 들면 다음과 같습니다 g++ -fno-omit-frame-pointer -O2 main.cpp
..
다음을 통해 애플리케이션의 “실시간”분석을 볼 수 있습니다 perf top
.
sudo perf top -p `pidof a.out` -K
또는 실행중인 응용 프로그램의 성능 데이터를 기록한 후 분석 할 수 있습니다.
1) 성능 데이터를 기록하려면 다음을 수행하십시오.
perf record -p `pidof a.out`
또는 10 초 동안 녹음하려면 :
perf record -p `pidof a.out` sleep 10
또는 호출 그래프로 기록 ()
perf record -g -p `pidof a.out`
2) 기록 된 데이터를 분석하려면
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
또는 애플리케이션의 성능 데이터를 기록한 후 애플리케이션을 이런 방식으로 시작하고 종료 될 때까지 기다린 후 분석 할 수 있습니다.
perf record ./a.out
이것은 테스트 프로그램을 프로파일 링하는 예입니다
테스트 프로그램은 main.cpp 파일에 있습니다 (메시지 하단에 main.cpp를 넣습니다).
이런 식으로 컴파일합니다.
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
libc malloc 이이 옵션없이 컴파일 된 것처럼 보이는 동안 사용 libmalloc_minimial.so
되므로 사용 합니다 -fno-omit-frame-pointer
. 그런 다음 테스트 프로그램을 실행합니다
./my_test 100000000
그런 다음 실행중인 프로세스의 성능 데이터를 기록합니다.
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
그런 다음 모듈 당 부하를 분석합니다.
성능 보고서 –stdio -g 없음-정렬 통신, dso -i ./my_test.perf.data
# Overhead Command Shared Object
# ........ ....... ............................
#
70.06% my_test my_test
28.33% my_test libtcmalloc_minimal.so.0.1.0
1.61% my_test [kernel.kallsyms]
그런 다음 기능별로드가 분석됩니다.
perf report –stdio -g none -i ./my_test.perf.data | c ++ 여과
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
29.14% my_test my_test [.] f1(long)
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
9.44% my_test my_test [.] process_request(long)
1.01% my_test my_test [.] operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
0.13% my_test [kernel.kallsyms] [k] native_write_msr_safe
and so on ...
그런 다음 콜 체인이 분석됩니다.
perf 보고서 –stdio -g 그래프 -i ./my_test.perf.data | c ++ 여과
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
|
--- f2(long)
|
--29.01%-- process_request(long)
main
__libc_start_main
29.14% my_test my_test [.] f1(long)
|
--- f1(long)
|
|--15.05%-- process_request(long)
| main
| __libc_start_main
|
--13.79%-- f2(long)
process_request(long)
main
__libc_start_main
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
|
--- operator new(unsigned long)
|
|--11.44%-- f1(long)
| |
| |--5.75%-- process_request(long)
| | main
| | __libc_start_main
| |
| --5.69%-- f2(long)
| process_request(long)
| main
| __libc_start_main
|
--3.01%-- process_request(long)
main
__libc_start_main
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
|
--- operator delete(void*)
|
|--9.13%-- f1(long)
| |
| |--4.63%-- f2(long)
| | process_request(long)
| | main
| | __libc_start_main
| |
| --4.51%-- process_request(long)
| main
| __libc_start_main
|
|--3.05%-- process_request(long)
| main
| __libc_start_main
|
--0.80%-- f2(long)
process_request(long)
main
__libc_start_main
9.44% my_test my_test [.] process_request(long)
|
--- process_request(long)
|
--9.39%-- main
__libc_start_main
1.01% my_test my_test [.] operator delete(void*)@plt
|
--- operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
|
--- operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
and so on ...
따라서이 시점에서 프로그램이 어디에 시간을 소비하는지 알 수 있습니다.
그리고 이것은 테스트를위한 main.cpp입니다 :
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t f1(time_t time_value)
{
for (int j =0; j < 10; ++j) {
++time_value;
if (j%5 == 0) {
double *p = new double;
delete p;
}
}
return time_value;
}
time_t f2(time_t time_value)
{
for (int j =0; j < 40; ++j) {
++time_value;
}
time_value=f1(time_value);
return time_value;
}
time_t process_request(time_t time_value)
{
for (int j =0; j < 10; ++j) {
int *p = new int;
delete p;
for (int m =0; m < 10; ++m) {
++time_value;
}
}
for (int i =0; i < 10; ++i) {
time_value=f1(time_value);
time_value=f2(time_value);
}
return time_value;
}
int main(int argc, char* argv2[])
{
int number_loops = argc > 1 ? atoi(argv2[1]) : 1;
time_t time_value = time(0);
printf("number loops %d\n", number_loops);
printf("time_value: %d\n", time_value );
for (int i =0; i < number_loops; ++i) {
time_value = process_request(time_value);
}
printf("time_value: %ld\n", time_value );
return 0;
}
답변
OProfile을 사용해보십시오 . 코드를 프로파일 링하는 데 훨씬 유용한 도구입니다. 나는 또한 Intel VTune을 제안 할 것이다 .
위의 두 도구는 특정 코드 행에 소요되는 시간을 좁히고 코드에 주석을 달며 어셈블리를 보여주고 특정 명령이 얼마나 걸리는지 보여줍니다. 시간 메트릭 외에도 캐시 카운터 등의 특정 카운터를 쿼리 할 수도 있습니다.
gprof와 달리 시스템에서 실행중인 모든 프로세스 / 이진을 프로파일 링 할 수 있습니다.