วิธีใช้ cProfile เพื่อสร้างโปรไฟล์โค้ด Python

Python อาจไม่ใช่ภาษาที่เร็วที่สุด แต่มักจะเร็วพอ และ Python เหมาะอย่างยิ่งเมื่อเวลาของโปรแกรมเมอร์มีความสำคัญมากกว่าเวลาของ CPU

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

วิธีใช้ cProfile

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

นี่คือตัวอย่างของเล่นวิธีใช้cProfile:

def เพิ่ม (x, y): x + = str (y) ส่งคืน x def add_2 (x, y): ถ้า y% 20000 == 0: z = [] สำหรับ q ในช่วง (0,400000): z.append ( q) def main (): a = [] สำหรับ n ในช่วง (0,200000): เพิ่ม (a, n) add_2 (a, n) ถ้า __name__ == '__main__': นำเข้า cProfile cProfile.run ('main ( ) ') 

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

เรียกใช้ตัวอย่างข้างต้นและคุณจะได้รับการต้อนรับด้วยผลลัพธ์ต่อไปนี้:

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

  • ที่ด้านบนสุด (บรรทัดแรกเป็นสีน้ำเงิน) เราจะเห็นจำนวนการโทรทั้งหมดในโปรแกรมที่ทำโปรไฟล์และเวลาดำเนินการทั้งหมด คุณอาจเห็นตัวเลขสำหรับ "การโทรแบบดั้งเดิม" ซึ่งหมายถึงการโทรแบบไม่เรียกซ้ำหรือการโทรที่ทำโดยตรงกับฟังก์ชันที่ไม่เรียกตัวเองลงไปในกลุ่มการโทร
  • ncalls : จำนวนการโทร หากคุณเห็นตัวเลขสองตัวคั่นด้วยเครื่องหมายทับตัวเลขที่สองคือจำนวนการเรียกดั้งเดิมสำหรับฟังก์ชันนั้น
  • tottime : เวลาทั้งหมดที่ใช้ในฟังก์ชันนี้ไม่รวมถึงการเรียกใช้ฟังก์ชันอื่น ๆ
  • percall : เวลาเฉลี่ยต่อการเรียกร้องให้tottimeมาโดยการtottimeแล้วหารด้วยncalls
  • cumtime : เวลาทั้งหมดที่ใช้ในฟังก์ชันรวมถึงการเรียกใช้ฟังก์ชันอื่น ๆ
  • percall (# 2): เวลาเฉลี่ยต่อการโทรสำหรับcumtime ( cumtimeหารด้วยncalls )
  • ชื่อไฟล์: lineno : ชื่อไฟล์หมายเลขบรรทัดและชื่อฟังก์ชันสำหรับการโทรที่เป็นปัญหา

วิธีแก้ไขรายงาน cProfile

โดยค่าเริ่มต้นจะจัดcProfileเรียงเอาต์พุตตาม "ชื่อมาตรฐาน" ซึ่งหมายความว่าจัดเรียงตามข้อความในคอลัมน์ด้านขวาสุด (ชื่อไฟล์หมายเลขบรรทัด ฯลฯ )

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

เราสามารถสร้างผลลัพธ์เหล่านี้ได้โดยเรียก  cProfile สิ่งที่แตกต่างกันเล็กน้อย สังเกตว่าส่วนล่างของโปรแกรมข้างต้นสามารถปรับใหม่ได้อย่างไรเพื่อจัดเรียงสถิติตามคอลัมน์อื่น (ในกรณีนี้ncalls):

ถ้า __name__ == '__main__': นำเข้า cProfile, pstats profiler = cProfile.Profile () profiler.enable () main () profiler.disable () stats = pstats.Stats (profiler) .sort_stats ('ncalls') stats.print_stats () 

ผลลัพธ์จะมีลักษณะดังนี้:

นี่คือวิธีการทำงานทั้งหมดนี้:

  • แทนที่จะดำเนินการคำสั่งด้วยวิธีการcProfile.run()ซึ่งไม่ยืดหยุ่นมากนักเราจึงสร้างออบเจ็กต์การทำโปรไฟล์, profiler.
  • เมื่อเราต้องการสร้างรายละเอียดการกระทำบางอย่างเราเรียก.enable()อินสแตนซ์วัตถุ profiler ก่อนจากนั้นเรียกใช้การดำเนินการจากนั้นเรียก.disable()ใช้ (นี่เป็นวิธีหนึ่งในการสร้างโปรไฟล์เฉพาะบางส่วนของโปรแกรม)
  • pstatsโมดูลจะใช้ในการจัดการกับผลที่เก็บรวบรวมโดยวัตถุ Profiler และพิมพ์ผลลัพธ์เหล่านั้น

การรวมออบเจ็กต์ profiler และpstatsช่วยให้เราสามารถจัดการกับข้อมูลโปรไฟล์ที่จับได้ตัวอย่างเช่นเพื่อจัดเรียงสถิติที่สร้างขึ้นแตกต่างกัน ในตัวอย่างนี้ใช้การจัด.sort_stats('ncalls')เรียงสถิติตามncallsคอลัมน์ มีตัวเลือกการจัดเรียงอื่น ๆ

วิธีใช้ผลลัพธ์ cProfile สำหรับการเพิ่มประสิทธิภาพ

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

ncalls

ข้อมูลชิ้นแรกและสำคัญที่สุดที่คุณสามารถค้นพบcProfileคือฟังก์ชันใดที่เรียกใช้บ่อยที่สุดโดยใช้ncallsคอลัมน์

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

ในตัวอย่างข้างต้นฟังก์ชันadd(และฟังก์ชันadd_2) ถูกเรียกซ้ำ ๆ ในวง การย้ายลูปเข้าสู่addฟังก์ชันเองหรือการแทรกaddฟังก์ชันทั้งหมดเข้าไปจะช่วยแก้ปัญหานี้ได้

tottime

รายละเอียดทางสถิติที่เป็นประโยชน์อีกประการหนึ่งซึ่งฟังก์ชันที่โปรแกรมใช้เวลาส่วนใหญ่ในการดำเนินการโดยใช้tottimeคอลัมน์

In the above example, the add_2 function uses a loop to simulate some expensive computation, which pushes its tottime score to the top. Any function with a high tottime score deserves a close look, especially if it is called many times or in a tight loop.

Note that you always need to consider the context in which the function is used. If a function has a high tottime but is only called once — for instance, only when the program starts — it is less likely to be a bottleneck. However, if you’re trying to reduce startup time, you’ll want to know whether a function called at startup is making everything else wait.

How to export cProfile data

If you want to use cProfile's generated statistics in more advanced ways, you can export them to a data file:

stats = pstats.Stats(profiler) stats.dump_stats('/path/to/stats_file.dat') 

This file can be read back in by using the pstats module, then sorted or displayed with pstats. The data can also be re-used by other programs. Two examples:

  • pyprof2calltree renders detailed visualizations of the program’s call graph and usage stats from profile data. This article provides a detailed real-world example of its use.
  • snakeviz also generates visualizations from cProfile data, but uses a different representation for the data — a “sunburst” rather than pyprof2calltree’s “flame” graph.

Beyond cProfile for Python profiling

cProfile is hardly the only way to profile a Python application. cProfile is certainly one of the most convenient ways, given that it’s bundled with Python. But others deserve attention.

โครงการหนึ่งpy-spyสร้างโปรไฟล์สำหรับแอปพลิเคชัน Python โดยสุ่มตัวอย่างกิจกรรมการโทร py-spyสามารถใช้เพื่อตรวจสอบแอป Python ที่กำลังทำงานอยู่โดยไม่ต้องหยุดและรีสตาร์ทและไม่ต้องแก้ไขโค้ดเบสเพื่อให้สามารถใช้กับแอปพลิเคชันที่ปรับใช้โปรไฟล์ได้ py-spyยังสร้างสถิติบางอย่างเกี่ยวกับค่าโสหุ้ยที่เกิดขึ้นโดยรันไทม์ของ Python (เช่นค่าโสหุ้ยการเก็บขยะ) ซึ่งcProfileไม่