การทำโปรไฟล์รหัส Python โดยใช้ timeit และ cProfile - KDnuggets

การทำโปรไฟล์รหัส Python โดยใช้ timeit และ cProfile – KDnuggets

โหนดต้นทาง: 2874649

การทำโปรไฟล์รหัส Python โดยใช้ timeit และ cProfile
ภาพโดยผู้เขียน
 

ในฐานะนักพัฒนาซอฟต์แวร์ คุณคงเคยได้ยินคำพูดนี้มาก่อน “การปรับให้เหมาะสมก่อนเวลาอันควรเป็นรากฐานของความชั่วร้ายทั้งหมด”—มากกว่าหนึ่งครั้ง—ในอาชีพการงานของคุณ แม้ว่าการปรับให้เหมาะสมอาจไม่มีประโยชน์มากนัก (หรือจำเป็นจริงๆ) สำหรับโปรเจ็กต์ขนาดเล็ก แต่การทำโปรไฟล์ก็มักจะมีประโยชน์ 

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

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

โมดูล timeit เป็นส่วนหนึ่งของไลบรารีมาตรฐานของ Python และมีฟังก์ชันอำนวยความสะดวกบางอย่างที่สามารถใช้เพื่อกำหนดเวลาตัวอย่างโค้ดสั้น ๆ ได้

มาดูตัวอย่างง่ายๆ ของการกลับรายการ Python เราจะวัดเวลาดำเนินการในการรับสำเนารายการแบบย้อนกลับโดยใช้:

  • reversed() ฟังก์ชันและ
  • การแบ่งส่วนรายการ 
>>> nums=[6,9,2,3,7]
>>> list(reversed(nums))
[7, 3, 2, 9, 6]
>>> nums[::-1]
[7, 3, 2, 9, 6]

 

วิ่ง timeit ที่ Command Line

คุณสามารถเรียกใช้ timeit ที่บรรทัดคำสั่งโดยใช้ไวยากรณ์:

$ python -m timeit -s 'setup-code' -n 'number' -r 'repeat' 'stmt'

 

คุณจะต้องจัดเตรียมใบแจ้งยอด stmt ซึ่งจะต้องวัดเวลาดำเนินการ 

คุณสามารถระบุ setup รหัสเมื่อจำเป็น - ใช้ตัวเลือกสั้น -s หรือตัวเลือกยาว - ตั้งค่า รหัสการตั้งค่าจะทำงานเพียงครั้งเดียว

พื้นที่ number จำนวนครั้งในการรันคำสั่ง: ตัวเลือกแบบสั้น -n หรือตัวเลือกแบบยาว –หมายเลข เป็นทางเลือก และจำนวนครั้งในการทำซ้ำรอบนี้: short option -r หรือ long option –repeat ก็เป็นทางเลือกเช่นกัน

เรามาดูการดำเนินการข้างต้นสำหรับตัวอย่างของเรา:

ที่นี่การสร้างรายการคือ setup รหัสและการย้อนกลับรายการคือคำสั่งที่ต้องกำหนดเวลา:

$ python -m timeit -s 'nums=[6,9,2,3,7]' 'list(reversed(nums))'
500000 loops, best of 5: 695 nsec per loop

 

เมื่อคุณไม่ได้ระบุค่าสำหรับ repeatจะใช้ค่าเริ่มต้นที่ 5 และเมื่อคุณไม่ระบุ numberโค้ดจะถูกรันหลาย ๆ ครั้งตามต้องการเพื่อให้ถึงเวลาทั้งหมด อย่างน้อย 0.2 วินาที.

ตัวอย่างนี้กำหนดจำนวนครั้งในการดำเนินการคำสั่งอย่างชัดเจน:

$ python -m timeit -s 'nums=[6,9,2,3,7]' -n 100Bu000 'list(reversed(nums))'
100000 loops, best of 5: 540 nsec per loop

 

ค่าเริ่มต้นของ repeat คือ 5 แต่เราสามารถตั้งค่าให้เป็นค่าที่เหมาะสมได้:

$ python3 -m timeit -s 'nums=[6,9,2,3,7]' -r 3 'list(reversed(nums))'
500000 loops, best of 3: 663 nsec per loop

 

เรามากำหนดเวลาวิธีการแบ่งส่วนรายการด้วย:

$ python3 -m timeit -s 'nums=[6,9,2,3,7]' 'nums[::-1]'
1000000 loops, best of 5: 142 nsec per loop

 

ดูเหมือนว่าวิธีการแบ่งส่วนรายการจะเร็วขึ้น (ตัวอย่างทั้งหมดอยู่ใน Python 3.10 บน Ubuntu 22.04)

รัน timeit ในสคริปต์ Python

นี่เทียบเท่ากับเวลาทำงานภายในสคริปต์ Python:

import timeit setup = 'nums=[9,2,3,7,6]'
number = 100000
stmt1 = 'list(reversed(nums))'
stmt2 = 'nums[::-1]' t1 = timeit.timeit(setup=setup,stmt=stmt1,number=number)
t2 = timeit.timeit(setup=setup,stmt=stmt2,number=number) print(f"Using reversed() fn.: {t1}")
print(f"Using list slicing: {t2}")

 

พื้นที่ timeit() callable ส่งคืนเวลาดำเนินการของ stmt for number ครั้ง โปรดสังเกตว่าเราสามารถระบุจำนวนครั้งในการรันหรือสร้างได้อย่างชัดเจน number ใช้ค่าเริ่มต้นที่ 1000000

Output >>
Using reversed() fn.: 0.08982690000000002
Using list slicing: 0.015550800000000004

 

ซึ่งจะรันคำสั่งโดยไม่ต้องทำซ้ำฟังก์ชันจับเวลาตามที่ระบุ number ของครั้งและส่งคืนเวลาดำเนินการ นอกจากนี้ยังค่อนข้างใช้กันทั่วไป time.repeat() และใช้เวลาขั้นต่ำดังภาพ:

import timeit setup = 'nums=[9,2,3,7,6]'
number = 100000
stmt1 = 'list(reversed(nums))'
stmt2 = 'nums[::-1]' t1 = min(timeit.repeat(setup=setup,stmt=stmt1,number=number))
t2 = min(timeit.repeat(setup=setup,stmt=stmt2,number=number)) print(f"Using reversed() fn.: {t1}")
print(f"Using list slicing: {t2}")

 

นี่จะเป็นการทำซ้ำขั้นตอนการเรียกใช้โค้ด number ครั้ง repeat จำนวนครั้งและส่งคืนเวลาดำเนินการขั้นต่ำ ที่นี่เรามีการทำซ้ำ 5 ครั้ง แต่ละครั้งมี 100000 ครั้ง

Output >>
Using reversed() fn.: 0.055375300000000016
Using list slicing: 0.015101400000000043

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

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

มาเรียนรู้วิธีสร้างโปรไฟล์สคริปต์ Python โดยใช้โมดูล cProfile (ซึ่งเป็นส่วนหนึ่งของไลบรารีมาตรฐาน Python ด้วย) 

พิจารณาสคริปต์ Python ต่อไปนี้:

# main.py
import time def func(num): for i in range(num): print(i) def another_func(num): time.sleep(num) print(f"Slept for {num} seconds") def useful_func(nums, target): if target in nums: return nums.index(target) if __name__ == "__main__": func(1000) another_func(20) useful_func([2, 8, 12, 4], 12)

 

ที่นี่เรามีสามฟังก์ชั่น:

  • func() ที่วนซ้ำช่วงตัวเลขแล้วพิมพ์ออกมา
  • another func() ที่มีการเรียกไปยัง sleep() ฟังก์ชัน
  • useful_func() ที่ส่งคืนดัชนีของหมายเลขเป้าหมายในรายการ (หากมีเป้าหมายอยู่ในรายการ)

ฟังก์ชันที่ระบุไว้ข้างต้นจะถูกเรียกใช้ทุกครั้งที่คุณเรียกใช้สคริปต์ main.py

เรียกใช้ cProfile ที่ Command Line

เรียกใช้ cProfile ที่บรรทัดคำสั่งโดยใช้:

python3 -m file-name.py

 

ที่นี่เราได้ตั้งชื่อไฟล์ main.py:

python3 -m main.py

 

การรันสิ่งนี้ควรให้ผลลัพธ์ต่อไปนี้:

 Output >> 0 ... 999 Slept for 20 seconds

 

และโปรไฟล์ดังต่อไปนี้:

 

การทำโปรไฟล์รหัส Python โดยใช้ timeit และ cProfile
 

ที่นี่ ncalls หมายถึงจำนวนการเรียกใช้ฟังก์ชันและ percall หมายถึงเวลาต่อการเรียกใช้ฟังก์ชัน ถ้ามูลค่าของ ncalls มีค่ามากกว่าหนึ่งแล้ว percall คือเวลาเฉลี่ยของการโทรทั้งหมด

เวลาดำเนินการของสคริปต์ถูกครอบงำโดย another_func ที่ใช้บิวท์อิน sleep การเรียกใช้ฟังก์ชัน (พักเป็นเวลา 20 วินาที) เราเห็นสิ่งนั้น print การเรียกใช้ฟังก์ชันก็ค่อนข้างแพงเช่นกัน 

การใช้ cProfile ในสคริปต์ Python

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

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

# main.py
import pstats
import time
import cProfile def func(num): for i in range(num): print(i) def another_func(num): time.sleep(num) print(f"Slept for {num} seconds") def useful_func(nums, target): if target in nums: return nums.index(target) if __name__ == "__main__": with cProfile.Profile() as profile: func(1000) another_func(20) useful_func([2, 8, 12, 4], 12) profile_result = pstats.Stats(profile) profile_result.print_stats()

 

มาดูโปรไฟล์เอาต์พุตที่สร้างขึ้นให้ละเอียดยิ่งขึ้น:

 

การทำโปรไฟล์รหัส Python โดยใช้ timeit และ cProfile
 

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

...
if __name__ == "__main__": with cProfile.Profile() as profile: func(1000) another_func(20) useful_func([2, 8, 12, 4], 12) profile_result = pstats.Stats(profile) profile_result.sort_stats(pstats.SortKey.TIME) profile_result.print_stats()

 

เมื่อคุณรันสคริปต์แล้ว คุณควรจะเห็นผลลัพธ์ที่จัดเรียงตามเวลา:

 

การทำโปรไฟล์รหัส Python โดยใช้ timeit และ cProfile

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

ประทับเวลา:

เพิ่มเติมจาก KD นักเก็ต

เรื่องเด่น 6-12 กันยายน: คุณอ่านไฟล์ Excel ด้วย Python หรือไม่? มีวิธีที่เร็วกว่า 1000 เท่า; 8 แนวคิดโครงการการเรียนรู้เชิงลึกสำหรับผู้เริ่มต้น

โหนดต้นทาง: 1866658
ประทับเวลา: กันยายน 13, 2021