פרופיל Python Code באמצעות timeit ו-cProfile - KDnuggets

פרופיל Python Code באמצעות timeit ו-cProfile – KDnuggets

צומת המקור: 2874649

פרופיל Python Code באמצעות timeit ו-cProfile
תמונה מאת המחבר
 

כמפתח תוכנה, סביר להניח ששמעת את הציטוט "אופטימיזציה מוקדמת היא שורש כל הרוע"- יותר מפעם אחת - בקריירה שלך. למרות שאופטימיזציה לא יכולה להיות מועילה במיוחד (או הכרחית לחלוטין) עבור פרויקטים קטנים, יצירת פרופילים מועיל לעתים קרובות. 

לאחר שסיימת לקודד מודול, מומלץ לעשות פרופיל של הקוד שלך כדי למדוד כמה זמן לוקח כל אחד מהסעיפים לביצוע. זה יכול לעזור לזהות ריחות קוד ולהנחות אופטימיזציות לשיפור איכות הקוד. אז תמיד פרופיל הקוד שלך לפני ביצוע אופטימיזציה!

כדי לבצע את הצעדים הראשונים, מדריך זה יעזור לך להתחיל עם יצירת פרופילים ב-Python - באמצעות המובנה זמן ו פרופיל c מודולים. תלמד להשתמש הן בממשק שורת הפקודה והן בכתובות השונות המקבילות בתוך סקריפטים של 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 בשורת הפקודה

אתה יכול לרוץ timeit בשורת הפקודה באמצעות התחביר:

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

 

אתה נדרש לספק את ההצהרה stmt שאת זמן הביצוע שלו יש למדוד. 

אתה יכול לציין את setup קוד בעת הצורך - באמצעות האפשרות הקצרה -s או האפשרות הארוכה -setup. קוד ההגדרה יופעל פעם אחת בלבד.

השמיים number של פעמים להפעיל את ההצהרה: אפשרות קצרה -n או האפשרות הארוכה -מספר היא אופציונלית. ומספר הפעמים לחזור על המחזור הזה: אפשרות קצרה -r או האפשרות הארוכה -חזרה היא גם אופציונלית.

בואו נראה את האמור לעיל בפעולה עבור הדוגמה שלנו:

כאן יצירת הרשימה היא 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

הנה המקבילה להפעלת timeit בתוך הסקריפט של 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 ל 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 איטית מדי. או שלפונקציה עשויה להיות לולאה שניתן להחליף בביטוי הבנה פיתוני יותר. 

בואו ללמוד כיצד ליצור פרופיל של סקריפטים של 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 בשורת הפקודה

הפעל את cProfile בשורת הפקודה באמצעות:

python3 -m file-name.py

 

כאן קראנו לקובץ main.py:

python3 -m main.py

 

הפעלת זה אמור לתת לך את הפלט הבא:

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

 

והפרופיל הבא:

 

פרופיל Python Code באמצעות 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 Code באמצעות 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 Code באמצעות timeit ו-cProfile

אני מקווה שהמדריך הזה יעזור לך להתחיל עם יצירת פרופילים ב-Python. זכרו תמיד, אופטימיזציות לעולם לא צריכות לבוא במחיר של קריאות. אם אתה מעוניין ללמוד על פרופילים אחרים, כולל חבילות Python של צד שלישי, בדוק את זה מאמר על פרופילי Python.
 
 
באלה פריה סי הוא מפתח וכותב טכני מהודו. היא אוהבת לעבוד בצומת של מתמטיקה, תכנות, מדעי נתונים ויצירת תוכן. תחומי העניין והמומחיות שלה כוללים DevOps, מדעי נתונים ועיבוד שפה טבעית. היא נהנית לקרוא, לכתוב, לקוד ולקפה! נכון לעכשיו, היא עובדת על למידה ומשתפת את הידע שלה עם קהילת המפתחים על ידי יצירת מדריכים, מדריכים, מאמרי דעה ועוד.
 

בול זמן:

עוד מ KDnuggets