ฉันจะโปรไฟล์รหัส C ++ ที่ทำงานบน Linux ได้อย่างไร


1816

ฉันมีแอปพลิเคชั่น C ++ ที่ทำงานบน Linux ซึ่งฉันอยู่ในขั้นตอนการปรับให้เหมาะสม ฉันจะระบุได้อย่างไรว่าส่วนใดของรหัสที่ทำงานช้า


27
หากคุณจะให้ข้อมูลเพิ่มเติมเกี่ยวกับกองการพัฒนาของคุณคุณอาจได้รับคำตอบที่ดีขึ้น มีโปรไฟล์จาก Intel และ Sun แต่คุณต้องใช้คอมไพเลอร์ นั่นคือตัวเลือกหรือไม่?
Nazgob

2
มันมีคำตอบแล้วในลิงค์ต่อไปนี้: stackoverflow.com/questions/2497211/ …
Kapil Gupta

4
คำตอบส่วนใหญ่คือcodeโปรไฟล์ อย่างไรก็ตามการผกผันของลำดับความสำคัญการกำหนดชื่อแทนแคชการช่วงชิงทรัพยากร ฯลฯ อาจเป็นปัจจัยในการเพิ่มประสิทธิภาพและประสิทธิภาพ ผมคิดว่าคนอ่านข้อมูลลงในรหัสของฉันช้า คำถามที่พบบ่อยอ้างอิงหัวข้อนี้
เสียงอึกทึกครึกครื้น


3
ฉันเคยใช้ pstack แบบสุ่มเวลาส่วนใหญ่จะพิมพ์สแต็กทั่วไปที่โปรแกรมใช้เวลาส่วนใหญ่ดังนั้นจึงชี้ไปที่คอขวด
Jose Manuel Gomez Alvarez

คำตอบ:


1406

หากเป้าหมายของคุณคือการใช้ผู้สร้างโปรไฟล์ใช้หนึ่งในสิ่งที่แนะนำ

อย่างไรก็ตามหากคุณกำลังรีบและคุณสามารถขัดจังหวะโปรแกรมของคุณด้วยตนเองภายใต้ดีบักเกอร์ในขณะที่มันทำงานช้าในเชิงอัตวิสัยมีวิธีง่ายๆในการค้นหาปัญหาด้านประสิทธิภาพ

เพียงแค่หยุดมันหลายครั้งและแต่ละครั้งดูที่สแตกการโทร หากมีรหัสบางอย่างที่เสียเวลาไป 20% หรือ 50% หรืออะไรก็ตามนั่นคือความน่าจะเป็นที่คุณจะจับมันในการกระทำของแต่ละตัวอย่าง นั่นคือเปอร์เซ็นต์ของตัวอย่างที่คุณจะเห็น ไม่มีการคาดเดาที่จำเป็นต้องมีการศึกษา หากคุณมีการเดาว่าปัญหาคืออะไรสิ่งนี้จะพิสูจน์หรือหักล้างได้

คุณอาจมีปัญหาประสิทธิภาพการทำงานที่แตกต่างกันหลายขนาด หากคุณล้างสิ่งใดสิ่งหนึ่งส่วนที่เหลือจะรับเปอร์เซ็นต์ที่ใหญ่กว่าและสังเกตได้ง่ายกว่าในการผ่านครั้งต่อไป เอฟเฟกต์การขยายนี้เมื่อรวมเข้าด้วยกันกับปัญหาต่าง ๆ สามารถนำไปสู่ปัจจัยเร่งความเร็วที่แท้จริง

Caveat : โปรแกรมเมอร์มักจะสงสัยเทคนิคนี้เว้นแต่พวกเขาจะใช้มันเอง พวกเขาจะบอกว่าผู้ให้ข้อมูลให้ข้อมูลนี้แก่คุณ แต่นั่นเป็นเรื่องจริงหากพวกเขาสุ่มตัวอย่างการโทรทั้งหมดและจากนั้นให้คุณตรวจสอบกลุ่มตัวอย่างแบบสุ่ม (บทสรุปเป็นที่ที่ข้อมูลเชิงลึกหายไป) กราฟการโทรไม่ได้ให้ข้อมูลเดียวกันกับคุณเพราะ

  1. พวกเขาไม่สรุปที่ระดับการสอนและ
  2. พวกเขาให้ข้อสรุปที่สับสนในการปรากฏตัวของการเรียกซ้ำ

พวกเขายังจะบอกว่ามันใช้งานได้กับโปรแกรมของเล่นเท่านั้นเมื่อจริง ๆ แล้วมันทำงานในโปรแกรมใด ๆ และดูเหมือนว่าจะทำงานได้ดีขึ้นในโปรแกรมที่ใหญ่กว่าเพราะพวกเขามักจะมีปัญหาในการค้นหามากขึ้น พวกเขาจะบอกว่าบางครั้งมันก็พบว่าสิ่งที่ไม่เกิดปัญหา แต่ที่เป็นจริงเฉพาะในกรณีที่คุณเห็นบางสิ่งบางอย่างในครั้งเดียว หากคุณพบปัญหาเกี่ยวกับตัวอย่างมากกว่าหนึ่งตัวอย่างมันเป็นเรื่องจริง

PSสิ่งนี้สามารถทำได้ในโปรแกรมแบบมัลติเธรดหากมีวิธีรวบรวมตัวอย่าง call-stack ของเธรดพูล ณ จุดหนึ่งเนื่องจากมีอยู่ใน Java

PPSโดยทั่วไปแล้วสิ่งที่เป็นนามธรรมที่คุณมีในซอฟต์แวร์ของคุณมีความเป็นไปได้สูงที่คุณจะพบว่านั่นเป็นสาเหตุของปัญหาด้านประสิทธิภาพ (และโอกาสในการรับความเร็ว)

เพิ่มเติม : อาจไม่ชัดเจน แต่เทคนิคการสุ่มตัวอย่างสแต็คทำงานได้ดีเท่า ๆ กันเมื่อมีการเรียกซ้ำ เหตุผลคือเวลาที่จะถูกบันทึกไว้โดยการลบคำสั่งนั้นประมาณโดยเศษส่วนของตัวอย่างที่มีมันโดยไม่คำนึงถึงจำนวนครั้งที่มันอาจเกิดขึ้นภายในตัวอย่าง

อีกข้อคัดค้านที่ฉันได้ยินบ่อยๆคือ: " มันจะหยุดสุ่มที่ไหนสักแห่งและมันจะทำให้เกิดปัญหาจริง " สิ่งนี้มาจากการมีแนวคิดก่อนว่าปัญหาแท้จริงคืออะไร คุณสมบัติที่สำคัญของปัญหาประสิทธิภาพคือการที่พวกเขาท้าทายความคาดหวัง การสุ่มตัวอย่างบอกคุณว่ามีปัญหาและปฏิกิริยาแรกของคุณคือไม่เชื่อ นั่นเป็นเรื่องธรรมดา แต่คุณสามารถมั่นใจได้ว่าหากพบปัญหามันเป็นเรื่องจริงและในทางกลับกัน

เพิ่ม : ให้ฉันอธิบายแบบเบย์ถึงวิธีการทำงาน สมมติว่ามีคำสั่งI(โทรหรืออย่างอื่น) ซึ่งอยู่ใน call stack เศษส่วนfของเวลา (และค่าใช้จ่ายที่มาก) เพื่อความง่ายสมมติว่าเราไม่รู้ว่าอะไรfคืออะไรแต่สมมติว่ามันคือ 0.1, 0.2, 0.3, ... 0.9, 1.0 และความน่าจะเป็นก่อนหน้าของความเป็นไปได้แต่ละข้อเหล่านี้คือ 0.1 ดังนั้นค่าใช้จ่ายเหล่านี้มีแนวโน้มเท่ากัน A-เบื้องต้น

แล้วสมมติว่าเราใช้เวลาเพียง 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                

คอลัมน์สุดท้ายบอกว่าตัวอย่างเช่นความน่าจะเป็นที่f> = 0.5 เท่ากับ 92% เพิ่มขึ้นจากสมมติฐานก่อนหน้านี้ที่ 60%

สมมติว่าสมมติฐานก่อนหน้านี้แตกต่างกัน สมมติว่าเราสมมติว่า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)เป็น 26% เพิ่มขึ้นจากสมมติฐานก่อนหน้านี้ที่ 0.6% Iดังนั้นเบส์ช่วยให้เราสามารถปรับปรุงประมาณการของเราที่น่าจะเป็นค่าใช้จ่ายของ หากจำนวนข้อมูลมีขนาดเล็กก็ไม่ได้บอกเราอย่างถูกต้องว่าต้นทุนคืออะไรเพียงว่ามีขนาดใหญ่พอที่จะแก้ไขได้

แต่วิธีการที่จะมองมันอีกเรียกว่ากฎของความสำเร็จ ถ้าคุณพลิกเหรียญ 2 ครั้งและมันเกิดขึ้นทั้งสองครั้งนั่นจะบอกอะไรคุณเกี่ยวกับน้ำหนักที่น่าจะเป็นของเหรียญ (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.3ที่เป็น3+/-1.4ตัวอย่าง.)


เพิ่ม : เพื่อให้ความรู้สึกที่เข้าใจง่ายสำหรับความแตกต่างระหว่างการวัดและการสุ่มสแต็คแบบสุ่ม:
ตอนนี้มี profilers ที่สุ่มตัวอย่างสแต็คแม้ในเวลานาฬิกาแขวน แต่สิ่งที่ออกมาคือการวัด (หรือเส้นทางร้อนหรือจุดร้อน "คอขวด" สามารถซ่อนได้ง่าย) สิ่งที่พวกเขาไม่แสดงให้คุณเห็น (และพวกเขาทำได้อย่างง่ายดาย) คือตัวอย่างที่แท้จริง และถ้าเป้าหมายของคุณคือค้นหาคอขวดจำนวนที่คุณต้องดูคือโดยเฉลี่ยแล้ว 2 หารด้วยเศษส่วนของเวลาที่ใช้ ดังนั้นหากใช้เวลา 30% ของเวลาเฉลี่ย 2 / .3 = 6.7 ตัวอย่างจะแสดงและโอกาสที่ 20 ตัวอย่างจะแสดงว่าเป็น 99.2%

นี่คือภาพประกอบนอกข้อมือของความแตกต่างระหว่างการตรวจวัดและตรวจสอบตัวอย่างสแต็ค คอขวดอาจเป็นหยดน้ำก้อนใหญ่แบบนี้หรือก้อนเล็ก ๆ จำนวนมากก็ไม่ได้ทำให้เกิดความแตกต่าง

ป้อนคำอธิบายรูปภาพที่นี่

การวัดเป็นแนวนอน มันบอกให้คุณทราบว่าส่วนย่อยของเวลาที่ใช้ไปเป็นพิเศษ การสุ่มตัวอย่างเป็นแนวตั้ง หากมีวิธีใดที่จะหลีกเลี่ยงสิ่งที่โปรแกรมทั้งหมดกำลังทำอยู่ในขณะนั้นและถ้าคุณเห็นมันในตัวอย่างที่สองแสดงว่าคุณพบคอขวด นั่นคือสิ่งที่ทำให้เกิดความแตกต่าง - เห็นเหตุผลทั้งหมดสำหรับเวลาที่ใช้ไปไม่ใช่แค่เท่าไหร่


292
โดยทั่วไปแล้วนี่เป็นตัวอย่างการสุ่มตัวอย่างของคนจนที่ยอดเยี่ยม แต่คุณเสี่ยงต่อการมีขนาดตัวอย่างน้อยเกินไปซึ่งอาจทำให้คุณได้ผลลัพธ์ที่ไม่จริง
Crashworks

100
@Crash: ฉันจะไม่อภิปรายส่วน "คนจน" :-) มันเป็นความจริงที่ความแม่นยำในการวัดทางสถิติต้องการตัวอย่างจำนวนมาก แต่มีเป้าหมายที่ขัดแย้งกันสองประการนั่นคือการวัดและตำแหน่งของปัญหา ฉันมุ่งเน้นไปที่สิ่งหลังซึ่งคุณต้องการความแม่นยำของตำแหน่งไม่ใช่ความแม่นยำในการวัด ตัวอย่างเช่นอาจมีฟังก์ชั่นกลางสแต็คเรียกใช้ฟังก์ชันเดียว A (); คิดเป็น 50% ของเวลา แต่สามารถใช้ในฟังก์ชั่นใหญ่อีก B พร้อมกับการโทรไปยัง A () อื่น ๆ ที่ไม่แพง บทสรุปที่แม่นยำของเวลาทำงานสามารถเป็นเงื่อนงำได้ แต่ตัวอย่างสแต็กอื่น ๆ ทั้งหมดจะระบุปัญหา
Mike Dunlavey

41
... โลกดูเหมือนว่าจะคิดว่ากราฟการโทรซึ่งมีหมายเหตุประกอบกับจำนวนการโทรและ / หรือเวลาเฉลี่ยนั้นดีพอ มันไม่ใช่. และส่วนที่น่าเศร้าก็คือสำหรับผู้ที่สุ่มตัวอย่าง call stack ข้อมูลที่มีประโยชน์มากที่สุดจะอยู่ตรงหน้าพวกเขา แต่พวกเขาก็ทิ้งมันไป
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 ดีมาก แต่โปรดเตือนว่ามันจะทำให้โปรแกรมของคุณช้าลง
neves

30
ลองใช้Gprof2Dotเพื่อหาทางเลือกที่น่าทึ่งในการแสดงภาพออก ./gprof2dot.py -f callgrind callgrind.out.x | dot -Tsvg -o output.svg
เซบาสเตียน

2
@neves ใช่ Valgrind ไม่ค่อยมีประโยชน์ในแง่ของความเร็วสำหรับการทำโปรไฟล์ "gstreamer" และ "opencv" แอปพลิเคชันแบบเรียลไทม์
กระตือรือร้น

1
stackoverflow.com/questions/375913/…เป็นโซลูตันบางส่วนสำหรับปัญหาความเร็ว
T Samuelnu Samuel

3
@ เซบาสเตียน: gprof2dotตอนนี้อยู่ที่นี่: github.com/jrfonseca/gprof2dot
John Zwinck

348

ฉันถือว่าคุณกำลังใช้ GCC สารละลายมาตรฐานจะมีโปรไฟล์gprof

โปรดเพิ่ม-pgในการรวบรวมก่อนทำโปรไฟล์:

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

ฉันไม่ได้พยายามมันยัง แต่ผมเคยได้ยินสิ่งที่ดีเกี่ยวกับgoogle-perftools มันคุ้มค่าที่จะลอง

คำถามที่เกี่ยวข้องที่นี่

ไม่กี่ buzzwords อื่น ๆ หากgprofไม่ได้ทำผลงานได้สำหรับคุณ: ValgrindอินเทลVTuneอาทิตย์DTrace


3
ฉันยอมรับว่า gprof เป็นมาตรฐานปัจจุบัน แม้ว่าจะเป็นหมายเหตุก็ตาม Valgrind ใช้สำหรับโปรไฟล์หน่วยความจำรั่วและแง่มุมอื่น ๆ ที่เกี่ยวข้องกับหน่วยความจำของโปรแกรมของคุณไม่ใช่เพื่อการเพิ่มประสิทธิภาพความเร็ว
Bill the Lizard

68
บิลในชุดเร่ร่อนคุณสามารถค้นหา callgrind และเทือกเขา ทั้งสองมีประโยชน์สวยปพลิเคชันรายละเอียด
Dario minonne

7
@ Bill-the-Lizard: ความคิดเห็นบางอย่างเกี่ยวกับgprof : stackoverflow.com/questions/1777556/alternatives-to-gprof/…
Mike Dunlavey

6
gprof -pg เป็นเพียงการประมาณของการทำโปรไฟล์ callstack มันแทรกการเรียก mcount เพื่อติดตามฟังก์ชั่นที่กำลังเรียกฟังก์ชั่นอื่น ๆ มันใช้การสุ่มตัวอย่างตามเวลามาตรฐานสำหรับเอ่อเวลา จากนั้นจะมีการสุ่มตัวอย่างจำนวนครั้งในฟังก์ชัน foo () กลับไปที่ผู้เรียก foo () เพื่อแสดงถึงจำนวนของการโทร ดังนั้นจึงไม่แยกความแตกต่างระหว่างการโทรของค่าใช้จ่ายที่แตกต่างกัน
Krazy Glew

1
ด้วยเสียงดังกราว / เสียงดังกังวาน ++ หนึ่งอาจพิจารณาใช้โปรไฟล์ของ CPU gperftools Caveat: ยังไม่ได้ทำด้วยตัวเอง
einpoklum

257

เมล็ดใหม่กว่า (เช่นล่าสุดอูบุนตูเมล็ด) มาพร้อมกับใหม่ 'perf' เครื่องมือ ( apt-get install linux-tools) AKA perf_events

เหล่านี้มาพร้อมกับเครื่องมือเก็บตัวอย่างแบบคลาสสิก ( man-page ) และกราฟเวลาที่ยอดเยี่ยม !

สิ่งสำคัญคือเครื่องมือเหล่านี้สามารถสร้างโปรไฟล์ระบบและไม่เพียง แต่กระบวนการทำโปรไฟล์ - พวกเขาสามารถแสดงการโต้ตอบระหว่างเธรดกระบวนการและเคอร์เนลและช่วยให้คุณเข้าใจการกำหนดตารางเวลาและการพึ่งพา 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.htmlและbrendangregg.com/ebpf.html
Andrew Stern

การแนะนำที่ดีอีกอย่างที่perfมีอยู่ในarchive.li/9r927#selection-767.126-767.271 (ทำไมเทพเจ้าเหล่านั้นจึงตัดสินใจที่จะลบหน้านั้นออกจากฐานความรู้ SO นั้นอยู่เหนือฉัน .... )
ragerdl

75

ฉันจะใช้ Valgrind และ Callgrind เป็นฐานสำหรับชุดเครื่องมือทำโปรไฟล์ของฉัน สิ่งสำคัญที่ควรทราบคือ Valgrind นั้นเป็นเสมือนเครื่อง:

(วิกิพีเดีย) Valgrind เป็นเสมือนเครื่องเสมือนที่ใช้เทคนิคการคอมไพล์แบบทันเวลา (JIT) รวมถึงการคอมไพล์ซ้ำแบบไดนามิก ไม่มีสิ่งใดจากโปรแกรมดั้งเดิมที่จะทำงานโดยตรงบนโฮสต์โปรเซสเซอร์ Valgrind จะแปลโปรแกรมเป็นแบบชั่วคราวและเรียบง่ายกว่าเดิมซึ่งเรียกว่า Intermediate Representation (IR) ซึ่งเป็นรูปแบบที่ใช้หน่วยประมวลผลกลางซึ่งใช้ SSA หลังจากการแปลงเครื่องมือ (ดูด้านล่าง) มีอิสระที่จะทำการแปลงใด ๆ ที่ต้องการใน IR ก่อนที่ Valgrind จะแปล IR กลับเป็นรหัสเครื่องและอนุญาตให้ตัวประมวลผลโฮสต์เรียกใช้

Callgrind เป็น profiler build ตามนั้น ประโยชน์หลักคือคุณไม่ต้องเรียกใช้ aplication เป็นเวลาหลายชั่วโมงเพื่อให้ได้ผลลัพธ์ที่น่าเชื่อถือ แม้แต่คนเดียวระยะที่สองจะเพียงพอที่จะได้รับผลลัพธ์ที่น่าเชื่อถือหินแข็งเพราะ Callgrind เป็นไม่ใช่ละเอียด Profiler

เครื่องมืออีกชิ้นที่สร้างจาก Valgrind คือ Massif ฉันใช้เพื่อโปรไฟล์การใช้หน่วยความจำฮีป มันใช้งานได้ดี มันช่วยให้คุณได้ภาพรวมของการใช้หน่วยความจำ - รายละเอียดข้อมูลอะไรที่เก็บเปอร์เซ็นต์หน่วยความจำอะไรและ WHO ใส่ไว้ที่นั่น ข้อมูลดังกล่าวมีอยู่ในช่วงเวลาที่แตกต่างกันของการเรียกใช้แอปพลิเคชัน


70

คำตอบในการรันvalgrind --tool=callgrindยังไม่สมบูรณ์หากไม่มีตัวเลือกบางตัว โดยปกติเราไม่ต้องการให้โปรไฟล์เริ่มต้นช้า 10 นาทีภายใต้ Valgrind และต้องการทำโปรไฟล์โปรแกรมของเราเมื่อทำงานบางอย่าง

นี่คือสิ่งที่ฉันแนะนำ เรียกใช้โปรแกรมก่อน:

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

ตอนนี้เมื่อมันทำงานและเราต้องการที่จะเริ่มต้นการทำโปรไฟล์เราควรทำงานในหน้าต่างอื่น:

callgrind_control -i on

สิ่งนี้จะเปิดการทำโปรไฟล์ หากต้องการปิดและหยุดงานทั้งหมดเราอาจใช้:

callgrind_control -k

ตอนนี้เรามีไฟล์ชื่อ callgrind.out. * ในไดเรกทอรีปัจจุบัน หากต้องการดูผลลัพธ์ของการทำโปรไฟล์ให้ทำดังนี้

kcachegrind callgrind.out.*

ฉันแนะนำในหน้าต่างถัดไปให้คลิกที่ส่วนหัวของคอลัมน์ "ตนเอง" มิฉะนั้นจะแสดงว่า "main ()" เป็นงานที่ต้องใช้เวลามากที่สุด "ตนเอง" แสดงให้เห็นว่าแต่ละฟังก์ชั่นใช้เวลามากน้อยเพียงใด


9
ด้วยเหตุผลบางอย่าง callgrind.out. * ไฟล์จะว่างเปล่าเสมอ การเรียกใช้ callgrind_control -d มีประโยชน์ในการบังคับให้ดัมพ์ข้อมูลไปยังดิสก์
T Samuelnu Samuel

3
ลาด. บริบทปกติของฉันมีทั้ง MySQL หรือ PHP หรือบางอย่างที่คล้ายกัน บ่อยครั้งที่ไม่ทราบว่าฉันต้องการแยกจากกันในตอนแรก
T Samuelnu Samuel

2
หรือในกรณีของฉันโปรแกรมของฉันโหลดข้อมูลจำนวนมากลงในแคช LRU และฉันไม่ต้องการให้โปรไฟล์นั้น ดังนั้นฉันจึงบังคับให้โหลดชุดย่อยของแคชเมื่อเริ่มต้นและโปรไฟล์รหัสโดยใช้ข้อมูลนั้นเท่านั้น (การปล่อยให้ OS + CPU จัดการการใช้หน่วยความจำภายในแคชของฉัน) ใช้งานได้ แต่การโหลดแคชช้าและใช้ CPU อย่างเข้มข้นในรหัสที่ฉันพยายามทำโปรไฟล์ในบริบทที่แตกต่างกันดังนั้น callgrind จึงสร้างผลลัพธ์ที่มีมลภาวะไม่ดี
รหัส

2
นอกจากนี้ยังมีCALLGRIND_TOGGLE_COLLECTการเปิดใช้งาน / ปิดใช้งานการรวบรวมโดยทางโปรแกรม ดูstackoverflow.com/a/13700817/288875
Andre Holzner

1
ว้าวฉันไม่เคยรู้เรื่องนี้มาก่อนขอบคุณ!
Vincent Fourmond

59

นี่คือการตอบสนองต่อNazgob คำตอบของ

ฉันใช้ Gprof เมื่อสองสามวันที่ผ่านมาและพบข้อ จำกัด ที่สำคัญสามข้อข้อหนึ่งซึ่งฉันยังไม่ได้บันทึกไว้ที่อื่น (ยัง):

  1. มันไม่ทำงานอย่างถูกต้องกับรหัสแบบมัลติเธรดเว้นแต่ว่าคุณจะใช้วิธีแก้ปัญหา

  2. กราฟการโทรทำให้สับสนโดยตัวชี้ฟังก์ชั่น ตัวอย่าง: ฉันมีฟังก์ชั่นที่เรียกว่าmultithread()ซึ่งช่วยให้ฉันสามารถมัลติฟังก์ชั่นที่ระบุมากกว่าอาร์เรย์ที่ระบุ (ทั้งผ่านเป็นอาร์กิวเมนต์) อย่างไรก็ตาม Gprof จะเรียกการเรียกทั้งหมดให้multithread()เท่ากับจุดประสงค์ในการคำนวณเวลาที่ใช้กับเด็ก ๆ เนื่องจากฟังก์ชั่นบางอย่างที่ฉันส่งผ่านmultithread()ใช้เวลานานกว่าฟังก์ชั่นการโทรอื่น ๆ ของฉันจึงไม่มีประโยชน์ (สำหรับผู้ที่สงสัยว่าเธรดเป็นปัญหาที่นี่: ไม่multithread()สามารถเลือกได้และในกรณีนี้ให้รันทุกอย่างตามลำดับบนเธรดการโทรเท่านั้น)

  3. มันบอกว่าที่นี่ว่า "... ตัวเลขของการโทรนั้นมาจากการนับไม่ใช่การสุ่มตัวอย่างพวกมันแม่นยำ ... " แต่ฉันก็พบว่ากราฟการโทรของฉันให้ฉัน 5345859132 + 784984078 เป็นสถิติการโทรของฟังก์ชั่นที่ฉันใช้บ่อยที่สุดซึ่งหมายเลขแรกควรจะเป็นการโทรโดยตรงและการโทรซ้ำครั้งที่สอง (ซึ่งมาจากตัวเอง) เนื่องจากสิ่งนี้บ่งบอกว่าฉันมีข้อผิดพลาดฉันจึงใส่ตัวนับยาว (64- บิต) ลงในรหัสและทำแบบเดียวกันอีกครั้ง จำนวนของฉัน: 5345859132 โดยตรงและ 78094395406 การโทรซ้ำด้วยตนเอง มีตัวเลขจำนวนมากอยู่ที่นั่นดังนั้นฉันจะชี้ให้เห็นว่าการโทรซ้ำที่ฉันวัดได้คือ 78 พันล้านเทียบกับ 784m จาก Gprof: ปัจจัยที่แตกต่างกัน 100 ประการ การรันทั้งสองเป็นโค้ดแบบเธรดเดี่ยวและแบบที่ไม่มีการปรับเวลาหนึ่งคอมไพล์-gและอีกอัน-pgหนึ่ง

นี่คือ GNU Gprof (GNU Binutils สำหรับ Debian) 2.18.0.20080103 ที่ทำงานภายใต้ Debian Lenny 64- บิตถ้าเป็นเช่นนั้น


ใช่มันทำการสุ่มตัวอย่าง แต่ไม่ใช่สำหรับหมายเลขที่โทรเข้า ที่น่าสนใจการติดตามลิงค์ของคุณในที่สุดก็นำฉันไปสู่หน้าเวอร์ชั่นที่ปรับปรุงแล้วซึ่งฉันเชื่อมโยงไปถึงในโพสต์ของฉัน URL ใหม่: sourceware.org/binutils/docs/gprof/… นี่เป็นการพูดซ้ำในส่วน (iii) ของคำตอบของฉัน แต่ยังบอกว่า "ในแอพพลิเคชั่นแบบมัลติเธรดหรือแอพพลิเคชั่นแบบเธรดเดี่ยวที่เชื่อมโยงกับไลบรารีแบบมัลติเธรดการนับจะกำหนดได้ก็ต่อเมื่อฟังก์ชันการนับเป็น thread-safe (หมายเหตุ: ระวังว่าฟังก์ชันการนับ mcount ใน glibc ไม่ใช่เธรด -Safe)."
Rob_before_edits

ไม่ชัดเจนสำหรับฉันหากสิ่งนี้อธิบายถึงผลลัพธ์ของฉันใน (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การโทรfastและmaybe_slow3 ครั้งหนึ่งในการ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 btGDB? ฟังก์ชั่นที่ขาดหายไปจากผลผลิต GProfฉันคิดว่าเป็นเพราะ gprof ยังทำการสุ่มตัวอย่างด้วยนอกเหนือจากการรวบรวมเครื่องมือและ-O3 mainมันก็เร็วเกินไปและไม่มีตัวอย่าง

ฉันเลือกเอาต์พุต SVG แทน PNG เพราะ SVG สามารถค้นหาได้ด้วย Ctrl + F และขนาดไฟล์อาจเล็กกว่าประมาณ 10 เท่า ยิ่งไปกว่านั้นความกว้างและความสูงของภาพที่สร้างขึ้นอาจมีขนาดใหญ่ถึงหมื่นพิกเซลสำหรับซอฟต์แวร์ที่ซับซ้อนและ GNOME eog3.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


2
โดยค่าเริ่มต้นระเบียน perf จะใช้การลงทะเบียนตัวชี้เฟรม คอมไพเลอร์สมัยใหม่จะไม่บันทึกที่อยู่เฟรมและใช้การลงทะเบียนแทนวัตถุประสงค์ทั่วไปแทน ทางเลือกคือการคอมไพล์ด้วยการ-fno-omit-frame-pointerตั้งค่าสถานะหรือใช้ทางเลือกอื่น: บันทึกด้วย--call-graph "dwarf"หรือ--call-graph "lbr"ขึ้นอยู่กับสถานการณ์ของคุณ
Jorge Bellon

5

สำหรับโปรแกรมเดียว threaded คุณสามารถใช้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 ) - หากคุณมีคอมไพเลอร์ intel นี่เป็นสิ่งที่ดีมาก
  3. TAU ( http://www.cs.uoregon.edu/research/tau/home.php )

ฉันใช้ HPCToolkit และ VTune และพวกเขามีประสิทธิภาพมากในการค้นหาเสายาวในเต็นท์และไม่จำเป็นต้องมีรหัสของคุณจะคอมไพล์ใหม่ (ยกเว้นว่าคุณต้องใช้ -g -O หรือ RelWithDebInfo ชนิด build ใน CMake เพื่อรับเอาต์พุตที่มีความหมาย) . ฉันได้ยินมาว่า TAU นั้นมีความสามารถคล้ายกัน


4

นี่คือสองวิธีที่ฉันใช้เพื่อเร่งโค้ดของฉัน:

สำหรับการใช้งาน CPU ที่ผูกไว้:

  1. ใช้ผู้สร้างโปรไฟล์ในโหมด DEBUG เพื่อระบุส่วนที่สงสัยของรหัสของคุณ
  2. จากนั้นสลับไปที่โหมด RELEASE และใส่ความคิดเห็นในส่วนที่สงสัยของรหัสของคุณ (อย่าทำอะไรเลย) จนกว่าคุณจะเห็นการเปลี่ยนแปลงของประสิทธิภาพ

สำหรับแอ็พพลิเคชันที่เชื่อมโยง I / O:

  1. ใช้ผู้สร้างโปรไฟล์ในโหมด RELEASE เพื่อระบุส่วนที่สงสัยของรหัสของคุณ

NB

หากคุณไม่มี profiler ให้ใช้ profiler ของชายยากจน กดปุ่มหยุดชั่วคราวขณะทำการดีบั๊กแอปพลิเคชันของคุณ ชุดนักพัฒนาซอฟต์แวร์ส่วนใหญ่จะแบ่งการชุมนุมด้วยหมายเลขบรรทัดที่แสดงความคิดเห็น คุณมีแนวโน้มที่จะลงจอดในภูมิภาคที่กินซีพียูเป็นส่วนใหญ่

สำหรับ CPU เหตุผลในการทำโปรไฟล์ในโหมดDEBUGคือเพราะถ้าคุณพยายามทำโปรไฟล์ในโหมดRELEASEคอมไพเลอร์จะลดคณิตศาสตร์, เวกเตอร์ลูปและฟังก์ชั่นอินไลน์ซึ่งมีแนวโน้มที่จะทำให้โค้ดของคุณยุ่งเหยิง ระเบียบยกเลิก mappable หมายถึงการสร้างโปรไฟล์ของคุณจะไม่สามารถที่จะระบุอย่างชัดเจนสิ่งที่เกิดนานมากเพราะการชุมนุมอาจจะไม่ตรงกับรหัสที่มาภายใต้การเพิ่มประสิทธิภาพ หากคุณต้องการประสิทธิภาพ (เช่นเวลาที่ไวต่อ) ของโหมดRELEASEให้ปิดการใช้งานคุณสมบัติดีบักเกอร์ตามต้องการเพื่อให้ประสิทธิภาพการใช้งานดีขึ้น

สำหรับ I / O-bound, ผู้สร้างโปรไฟล์ยังสามารถระบุการดำเนินการ I / O ในโหมดRELEASEเนื่องจากการดำเนินการ I / O นั้นเชื่อมโยงภายนอกกับห้องสมุดสาธารณะที่ใช้ร่วมกัน (ส่วนใหญ่เวลา) หรือในกรณีที่เลวร้ายที่สุดจะส่งผลให้ call interrupt vector (ซึ่งสามารถระบุได้ง่ายโดย profiler)


2
+1 วิธีการของคนจนทำงานได้ดีสำหรับ I / O ที่ถูกผูกไว้สำหรับ CPU ที่ถูกผูกไว้และฉันขอแนะนำให้ทำการปรับแต่งประสิทธิภาพทั้งหมดในโหมด DEBUG เมื่อคุณปรับจูนเสร็จแล้วให้เปิด RELEASE มันจะทำการปรับปรุงหากโปรแกรมนั้นทำงานกับ CPU ในรหัสของคุณ นี่คือวิดีโอที่หยาบ แต่สั้นของกระบวนการ
Mike Dunlavey

3
ฉันจะไม่ใช้ DEBUG builds สำหรับการทำโปรไฟล์ประสิทธิภาพ ฉันมักจะเห็นว่าส่วนสำคัญในการปฏิบัติงานในโหมด DEBUG นั้นได้รับการปรับให้เหมาะสมที่สุดในโหมดปล่อย ปัญหาอีกประการคือการใช้ asserts ในรหัส debug ซึ่งเพิ่มเสียงรบกวนให้กับประสิทธิภาพ
gast128

3
คุณอ่านโพสต์ของฉันได้ทั้งหมดหรือไม่? "หากคุณต้องการประสิทธิภาพ (เช่นเวลาที่ไวต่อ) ของโหมด RELEASE ให้ปิดการใช้งานคุณสมบัติดีบักเกอร์ตามต้องการเพื่อรักษาประสิทธิภาพการใช้งาน", "จากนั้นสลับไปที่โหมด RELEASE และแสดงความคิดเห็นในส่วนที่สงสัยของรหัสของคุณ การเปลี่ยนแปลงประสิทธิภาพ "? ฉันพูดว่าตรวจสอบพื้นที่ปัญหาที่เป็นไปได้ในโหมด debug และตรวจสอบปัญหาเหล่านั้นในโหมด release เพื่อหลีกเลี่ยงการผิดพลาดที่คุณกล่าวถึง
seo

2

คุณสามารถใช้ไลบรารี iprof:

https://gitlab.com/Neurochrom/iprof

https://github.com/Neurochrom/iprof

มันเป็นแพลตฟอร์มข้ามและช่วยให้คุณไม่ได้วัดประสิทธิภาพของแอปพลิเคชันของคุณแบบเรียลไทม์ คุณยังสามารถจับคู่มันด้วยกราฟสด ข้อจำกัดความรับผิดชอบฉบับเต็ม: ฉันเป็นผู้เขียน


2

คุณสามารถใช้เฟรมเวิร์กการบันทึกเช่นloguruเนื่องจากมีการประทับเวลาและเวลาทั้งหมดที่สามารถใช้ในการทำโปรไฟล์ได้:


1

ที่ทำงานเรามีเครื่องมือที่ดีมากที่ช่วยให้เราตรวจสอบสิ่งที่เราต้องการในแง่ของการกำหนดเวลา สิ่งนี้มีประโยชน์หลายครั้ง

มันอยู่ใน C ++ และจะต้องปรับแต่งตามความต้องการของคุณ น่าเสียดายที่ฉันไม่สามารถแบ่งปันรหัสได้เป็นเพียงแนวคิด คุณใช้volatileบัฟเฟอร์"ขนาดใหญ่" ที่มีการประทับเวลาและรหัสเหตุการณ์ที่คุณสามารถถ่ายโอนโพสต์ชันสูตรหรือหลังจากหยุดระบบการบันทึก (และการถ่ายโอนข้อมูลนี้ลงในไฟล์ตัวอย่าง)

คุณเรียกค้นบัฟเฟอร์ขนาดใหญ่ที่มีข้อมูลทั้งหมดและอินเทอร์เฟซขนาดเล็กแยกวิเคราะห์และแสดงเหตุการณ์ที่มีชื่อ (ขึ้น / ลง + ค่า) เช่นสโคปทำด้วยสี (กำหนดค่าใน.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ด้วยการรวมกันของcallrindและkcachegrindควรให้การประเมินที่เหมาะสมในประเด็นข้างต้นและเมื่อมันได้รับการยอมรับว่ามีปัญหากับบางส่วนของรหัสฉันขอแนะนำให้ทำเครื่องหมายมาตรฐานขนาดเล็ก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 ฉันจะเพิ่มมันเป็นการส่วนตัวที่ฉันใช้แผนที่เพื่อทำโปรไฟล์โปรแกรมวิทยาศาสตร์ C ++ ได้สำเร็จ

ARM MAP เป็นตัวสร้างโปรไฟล์สำหรับโค้ด C, C ++, Fortran และ F90 แบบขนาน, มัลติเธรดหรือเธรดเดี่ยว มันมีการวิเคราะห์เชิงลึกและการระบุจุดคอขวดในบรรทัดแหล่งที่มา ไม่เหมือน profilers ส่วนใหญ่มันถูกออกแบบมาเพื่อให้สามารถโพรไฟล์ 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.