สำรวจเทคนิคการทำโปรไฟล์ C ++
ในคำตอบนี้ฉันจะใช้เครื่องมือต่าง ๆ ในการวิเคราะห์โปรแกรมทดสอบง่ายๆสองสามชุดเพื่อเปรียบเทียบการทำงานของเครื่องมือเหล่านั้นอย่างเป็นรูปธรรม
โปรแกรมทดสอบต่อไปนี้ง่ายมากและทำสิ่งต่อไปนี้:
main
การโทรfast
และmaybe_slow
3 ครั้งหนึ่งในการmaybe_slow
โทรช้า
การเรียกช้ากว่าmaybe_slow
นั้นนานกว่า 10x และควบคุมการทำงานของรันไทม์หากเราพิจารณาการเรียกไปยังฟังก์ชันcommon
ย่อย ตามหลักแล้วเครื่องมือการทำโปรไฟล์จะสามารถชี้เราไปยังการโทรช้าที่เฉพาะเจาะจงได้
ทั้งสองfast
และการmaybe_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 ดังนั้นสิ่งที่เราต้องทำคือรวบรวม-pg
ตัวเลือกเพื่อเปิดใช้งาน gprof จากนั้นเราเรียกใช้โปรแกรมตามปกติด้วยพารามิเตอร์ขนาด CLI ที่สร้างระยะเวลาที่เหมาะสมในไม่กี่วินาที ( 10000
):
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
ผลผลิตที่สวยมากอธิบายตนเอง ตัวอย่างเช่นมันแสดงให้เห็นว่าการmaybe_slow
โทร3 ครั้งและการเรียกลูกของพวกเขาใช้เวลาถึง 97.56% ของรันไทม์ทั้งหมดแม้ว่าการดำเนินการของmaybe_slow
ตัวเองโดยไม่มีบัญชีย่อยคิดเป็น 0.00% ของเวลาดำเนินการทั้งหมดนั่นคือใช้เวลาเกือบทั้งหมดในฟังก์ชั่นนั้น เด็กโทร
สิ่งที่ต้องทำ: เหตุใดจึงmain
ขาดหายไปจาก-O3
ผลลัพธ์แม้ว่าฉันจะเห็นมันใน a bt
GDB? ฟังก์ชั่นที่ขาดหายไปจากผลผลิต GProfฉันคิดว่าเป็นเพราะ gprof ยังทำการสุ่มตัวอย่างด้วยนอกเหนือจากการรวบรวมเครื่องมือและ-O3
main
มันก็เร็วเกินไปและไม่มีตัวอย่าง
ฉันเลือกเอาต์พุต SVG แทน PNG เพราะ SVG สามารถค้นหาได้ด้วย Ctrl + F และขนาดไฟล์อาจเล็กกว่าประมาณ 10 เท่า ยิ่งไปกว่านั้นความกว้างและความสูงของภาพที่สร้างขึ้นอาจมีขนาดใหญ่ถึงหมื่นพิกเซลสำหรับซอฟต์แวร์ที่ซับซ้อนและ GNOME eog
3.28.1 มีข้อบกพร่องสำหรับ PNGs ในขณะที่เบราว์เซอร์ SVG เปิดขึ้นโดยอัตโนมัติ gimp 2.8 ทำงานได้ดีแม้ว่าดูเพิ่มเติมที่:
แต่ถึงอย่างนั้นคุณก็จะลากภาพไปรอบ ๆ เพื่อค้นหาสิ่งที่คุณต้องการดูตัวอย่างเช่นภาพจากซอฟต์แวร์ "ของจริง" ที่ถ่ายจากตั๋วนี้ :
คุณจะพบสายเรียกซ้อนที่สำคัญที่สุดได้อย่างง่ายดายด้วยเส้นสปาเก็ตตี้เล็ก ๆ ที่ยังไม่เรียงกันที่เรียงซ้อนกันหรือไม่ อาจมีdot
ตัวเลือกที่ดีกว่าฉันแน่ใจ แต่ฉันไม่ต้องการไปที่นั่นตอนนี้ สิ่งที่เราต้องการจริงๆเป็นตัวแสดงที่เหมาะสมสำหรับมัน แต่ฉันยังไม่พบ:
อย่างไรก็ตามคุณสามารถใช้แผนที่สีเพื่อลดปัญหาเหล่านั้นได้เล็กน้อย ตัวอย่างเช่นในภาพขนาดใหญ่ก่อนหน้านี้ในที่สุดฉันก็สามารถค้นหาเส้นทางวิกฤตทางซ้ายเมื่อฉันทำการลดทอนที่ยอดเยี่ยมที่สีเขียวมาจากสีแดงตามมาในที่สุดก็ตามด้วยสีเข้มและสีน้ำเงินเข้มขึ้น
นอกจากนี้เรายังสามารถสังเกตผลลัพธ์ข้อความของgprof
เครื่องมือ binutils ในตัวที่เราบันทึกไว้ก่อนหน้านี้ได้ที่:
cat main.gprof
โดยค่าเริ่มต้นสิ่งนี้จะสร้างเอาต์พุต verbose อย่างมากซึ่งจะอธิบายความหมายของข้อมูลเอาต์พุต เนื่องจากฉันไม่สามารถอธิบายได้ดีไปกว่านั้นฉันจะให้คุณอ่านเอง
เมื่อคุณเข้าใจรูปแบบการแสดงผลข้อมูลแล้วคุณสามารถลดการฟุ่มเฟื่อยเพื่อแสดงข้อมูลโดยไม่ต้องใช้-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_slow
และfast
ไม่มีผู้ปกครองที่รู้จักซึ่งเป็นสิ่งที่เอกสารกล่าวว่า<spontaneous>
หมายถึง
ฉันไม่แน่ใจว่ามีวิธีที่ดีในการทำโปรไฟล์แบบบรรทัดต่อบรรทัดด้วย gprof หรือไม่: เวลา“ gprof ” ที่ใช้ในบรรทัดรหัสเฉพาะ
valgrind callgrind
valgrind รันโปรแกรมผ่านเครื่องเสมือน valgrind สิ่งนี้ทำให้การจัดทำโปรไฟล์มีความแม่นยำมาก แต่ก็ทำให้โปรแกรมมีขนาดใหญ่มาก ฉันยังได้กล่าวถึง kcachegrind ก่อนหน้านี้ที่: เครื่องมือในการรับฟังก์ชั่นภาพกราฟการโทรของรหัส
callgrind เป็นเครื่องมือของ valgrind สำหรับรหัสโปรไฟล์และ kcachegrind เป็นโปรแกรม KDE ที่สามารถเห็นภาพเอาต์พุต cachegrind
ก่อนอื่นเราต้องลบ-pg
แฟล็กเพื่อกลับไปที่การคอมไพล์ปกติมิฉะนั้นการรันจะล้มเหลวด้วยProfiling timer expired
และใช่นี่เป็นเรื่องธรรมดามากที่ฉันทำและมีคำถาม Stack Overflow สำหรับมัน
ดังนั้นเราจึงรวบรวมและเรียกใช้เป็น:
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 วินาทีในการดำเนินการดังนั้นเราจึงมีการชะลอตัวประมาณ 15x ในตัวอย่างนี้ เห็นได้ชัดว่าการชะลอตัวนี้จะเป็นข้อ จำกัด ร้ายแรงสำหรับปริมาณงานที่มากขึ้น ในตัวอย่างซอฟต์แวร์ "โลกแห่งความจริง" ที่กล่าวถึงที่นี่ฉันพบว่าการชะลอตัวของ 80x
การรันจะสร้างไฟล์ข้อมูลโปรไฟล์ชื่อcallgrind.out.<pid>
เช่นcallgrind.out.8554
ในกรณีของฉัน เราดูไฟล์นั้นด้วย:
kcachegrind callgrind.out.8554
ซึ่งแสดง GUI ที่มีข้อมูลคล้ายกับเอาต์พุต gprof แบบข้อความ:
นอกจากนี้หากเราไปที่แท็บ "กราฟการโทร" ที่ด้านล่างขวาเราจะเห็นกราฟการโทรซึ่งเราสามารถส่งออกโดยคลิกขวาเพื่อรับภาพต่อไปนี้พร้อมกับจำนวนเส้นขอบสีขาวที่ไม่สมเหตุสมผล :-)
ฉันคิดว่าfast
ไม่แสดงในกราฟนั้นเพราะ kcachegrind ต้องทำให้การสร้างภาพง่ายขึ้นเพราะการโทรนั้นใช้เวลาน้อยเกินไปนี่อาจเป็นพฤติกรรมที่คุณต้องการในโปรแกรมจริง เมนูคลิกขวามีการตั้งค่าบางอย่างเพื่อควบคุมเวลาที่จะกำจัดโหนดดังกล่าว แต่ฉันไม่สามารถรับมันเพื่อแสดงการโทรสั้น ๆ หลังจากพยายามอย่างรวดเร็ว ถ้าฉันคลิกfast
ที่หน้าต่างด้านซ้ายมันจะแสดงกราฟการโทรด้วยfast
ดังนั้นสแต็กจะถูกจับจริง ยังไม่มีใครพบวิธีแสดงกราฟการโทรกราฟที่สมบูรณ์: ทำการโทรเพื่อรับสายแสดงการเรียกใช้ฟังก์ชันทั้งหมดใน kcachegrind callgraph
สิ่งที่ต้องทำในซอฟต์แวร์ C ++ ที่ซับซ้อนฉันเห็นรายการบางประเภท<cycle N>
เช่น<cycle 11>
ที่ฉันคาดหวังชื่อฟังก์ชั่นนั่นหมายความว่าอย่างไร ฉันสังเกตเห็นว่ามีปุ่ม "ตรวจจับวงจร" เพื่อสลับเปิดและปิด แต่หมายความว่าอย่างไร
perf
จาก linux-tools
perf
ดูเหมือนว่าจะใช้กลไกการสุ่มตัวอย่างลินุกซ์เคอร์เนลโดยเฉพาะ สิ่งนี้ทำให้การติดตั้งง่ายมาก แต่ก็ไม่แม่นยำอย่างสมบูรณ์
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
สิ่งที่ต้องทำ: เกิดอะไรขึ้นกับการ-O3
ประหารชีวิต? มันง่ายmaybe_slow
และfast
เร็วเกินไปและไม่ได้ตัวอย่างอะไรเลย? มันทำงานได้ดีกับ-O3
โปรแกรมขนาดใหญ่ที่ใช้เวลาในการรันนานกว่า? ฉันพลาดตัวเลือก CLI บ้างไหม? ฉันค้นพบเกี่ยวกับ-F
การควบคุมความถี่ตัวอย่างในเฮิร์ตซ์ แต่ฉันกลับไปหาค่าสูงสุดที่อนุญาตโดยค่าเริ่มต้นของ-F 39500
(สามารถเพิ่มได้ด้วยsudo
) และฉันยังไม่เห็นสายที่ชัดเจน
สิ่งที่ยอดเยี่ยมอย่างหนึ่งperf
คือเครื่องมือ FlameGraph จากเบรนแดนเกร็กซึ่งแสดงการกำหนดเวลาการโทรซ้อนในแบบที่เป็นระเบียบซึ่งช่วยให้คุณเห็นการโทรครั้งใหญ่ได้อย่างรวดเร็ว เครื่องมือนี้มีให้ที่: https://github.com/brendangregg/FlameGraphและยังได้กล่าวถึงในแบบฝึกหัด perf ของเขาที่: 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
ในกราฟที่:
ในตัวอย่างที่ซับซ้อนยิ่งขึ้นมันจะกลายเป็นความชัดเจนในสิ่งที่กราฟหมายถึง:
สิ่งที่ต้องทำมีบันทึกของ[unknown]
ฟังก์ชั่นในตัวอย่างนั้นทำไมเป็นอย่างนั้น?
อินเทอร์เฟซ GUI แบบ perf อื่นซึ่งอาจคุ้มค่า:
ปลั๊กอินเสริม Eclipse Trace Compass: https://www.eclipse.org/tracecompass/
แต่สิ่งนี้มีข้อเสียที่คุณต้องแปลงข้อมูลเป็นรูปแบบการสืบค้นกลับทั่วไปซึ่งสามารถทำได้ด้วยperf data --to-ctf
แต่จะต้องเปิดใช้งานที่เวลาการสร้าง / มีperf
ใหม่เพียงพอซึ่งอาจไม่ใช่กรณีสำหรับ perf ใน Ubuntu 18.04
https://github.com/KDAB/hotspot
ข้อเสียของสิ่งนี้คือดูเหมือนจะไม่มีแพ็คเกจของ Ubuntu และการสร้างมันต้องใช้ Qt 5.10 ในขณะที่ Ubuntu 18.04 นั้นอยู่ที่ Qt 5.9
gperftools
ก่อนหน้านี้เรียกว่า "เครื่องมือประสิทธิภาพของ Google" ที่มา: https://github.com/gperftools/gperftoolsตัวอย่างตาม
ก่อนติดตั้ง gperftools ด้วย:
sudo apt install google-perftools
จากนั้นเราสามารถเปิดใช้งานตัวสร้างโปรไฟล์ของ gperftools ได้สองวิธี: ที่รันไทม์หรือเวลาที่สร้าง
ที่รันไทม์เราต้องผ่านการตั้งค่า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-tool-tool) และใช้ kcachegrind เพื่อดูว่า:
google-pprof --callgrind main.out prof.out > callgrind.out
kcachegrind callgrind.out
หลังจากรันด้วยวิธีใดวิธีหนึ่งเราจะได้รับprof.out
ไฟล์ข้อมูลโปรไฟล์เป็นผลลัพธ์ เราสามารถดูไฟล์นั้นในรูปแบบ SVG ด้วย:
google-pprof --web main.out prof.out
ซึ่งให้กราฟการโทรที่คุ้นเคยเหมือนกับเครื่องมืออื่น ๆ แต่มีหน่วยตัวอย่างจำนวน clunky มากกว่าวินาที
นอกจากนี้เรายังสามารถรับข้อมูลต้นฉบับเดิมด้วย:
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
ดูเพิ่มเติม: วิธีใช้เครื่องมือ google perf
ทดสอบใน Ubuntu 18.04, gprof2dot 2019.11.30, valgrind 3.13.0, perf 4.15.18, เคอร์เนล Linux 4.15.0, FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b, gperftools 2.5-2