از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
روش استفاده از ابزارهای پروفایل داخلی پایتون: مثال و بهترین روشها

سرفصلهای مطلب
Python
به دلیل سادگی و خوانایی شناخته شده است و آن را در بین توسعه دهندگان مورد علاقه قرار می دهد. اما این سادگی گاهی به هزینه عملکرد می رسد. هنگامی که برنامه Python شما رشد می کند یا نیاز به تحمل بار کاری بزرگتر دارد ، درک آنچه در زیر کاپوت اتفاق می افتد بسیار مهم است.
در حالی که بسیاری از توسعه دهندگان به ابزارهای پروفایل شخص ثالث می رسند ، کتابخانه استاندارد پایتون در حال حاضر با قابلیت های پروفایل قدرتمندی که غالباً مورد غفلت یا کمبود قرار می گیرند ، بسته بندی شده است.
در این مقاله ، شما یاد می گیرید که چگونه از این ابزارهای پروفایل داخلی فراتر از استفاده اساسی آنها استفاده کنید. شما می توانید روش ترکیب و استفاده از آنها را برای به دست آوردن بینش عمیق در مورد عملکرد کد خود بدون نصب بسته های اضافی کشف کنید.
فهرست مطالب
-
پیش نیازهای
-
پروفایل داخلی زرادخانه
-
در
timeit
ماژول -
در
cProfile
ماژول -
در
pstats
ماژول -
در
profile
ماژول
-
-
آزمایشات عملی
-
راه اندازی
-
آزمایش 1: Basic vs Advanced
timeit
استفاده -
آزمایش 2: مؤثر
c
Profile
تجزیه -
آزمایش 3: ترکیب ابزار برای پروفایل در دنیای واقعی
-
-
بهترین روشها
-
پایان
-
منابع و خواندن بیشتر
پیش نیازهای
قبل از غواصی به تکنیک های پروفایل ، حتماً:
-
پایتون 3.6+: تمام نمونه های این مقاله با نسخه های Python 3.6 و جدیدتر سازگار است.
-
دانش اساسی پایتون: شما باید با اصول پایتون مانند توابع ، ماژول ها و ساختار داده های اساسی راحت باشید.
-
یک محیط آزمایش: یا یک محیط محلی پایتون یا یک محیط مجازی که در آن می توانید نمونه های کد را اجرا کنید.
برای این آموزش هیچ کتابخانه خارجی لازم نیست زیرا ما به طور انحصاری تمرکز خواهیم کرد روی ابزارهای پروفایل داخلی پایتون. می توانید نسخه Python خود را از این طریق تأیید کنید:
# Verify your Python version
import sys
print(f"Python version: {sys.version}")
پروفایل داخلی زرادخانه
پایتون با چندین ابزار پروفایل در کتابخانه استاندارد خود حمل می کند. بیایید هرکدام را کشف کنیم و نقاط قوت مختلف آنها را درک کنیم.
در timeit
ماژول
بیشتر توسعه دهندگان پایتون با پایه آشنا هستند timeit
استفاده:
import timeit
# Basic usage
execution_time = timeit.timeit('"-".join(str(n) for n in range(100))', number=1000)
print(f"Execution time: {execution_time} seconds")
# Sample output:
# Execution time: 0.006027 seconds
این مثال اساسی اندازه گیری می کند که مدت زمان لازم برای پیوستن به 100 عدد به یک رشته با Hyphens طول می کشد. در number=1000
پارامتر به Python می گوید که این عملیات را 1000 بار اجرا کند و زمان اجرای کل را برگرداند ، که به میانگین هرگونه نوسانات تصادفی کمک می کند.
با این حال ، timeit
انعطاف پذیری بسیار بیشتری را نسبت به بیشتر توسعه دهندگان ارائه می دهد. بیایید روشهای قدرتمندی برای استفاده از آن را کشف کنیم:
جداسازی کد تنظیم:
setup_code = """
data = [i for i in range(1000)]
"""
test_code = """
result = [x * 2 for x in data]
"""
execution_time = timeit.timeit(stmt=test_code, setup=setup_code, number=100)
print(f"Execution time: {execution_time} seconds")
# Sample output:
# Execution time: 0.001420 seconds
در این مثال ، کد تنظیم را از کد به موقع جدا می کنیم. این بسیار مفید است وقتی:
-
شما باید داده های تست ایجاد کنید اما آن زمان را در اندازه گیری خود نمی خواهید
-
شما در حال انجام عملکردی هستید که متکی باشد روی واردات یا تعاریف متغیر
-
شما می خواهید از همان تنظیمات برای چندین تست زمان بندی استفاده مجدد کنید
مزیت این است که فقط کد در test_code
به پایان رسیده است ، در حالی که تنظیمات فقط یک بار قبل از شروع زمان اجرا می شود.
مقایسه توابع:
def approach_1(data):
return [x * 2 for x in data]
def approach_2(data):
return list(map(lambda x: x * 2, data))
data = list(range(1000))
time1 = timeit.timeit(lambda: approach_1(data), number=100)
time2 = timeit.timeit(lambda: approach_2(data), number=100)
print(f"Approach 1: {time1} seconds")
print(f"Approach 2: {time2} seconds")
print(f"Ratio: {time2/time1:.2f}x")
# Sample output:
# Approach 1: 0.001406 seconds
# Approach 2: 0.003049 seconds
# Ratio: 2.17x
این مثال نشان می دهد که چگونه می توان دو اجرای متفاوت از عملکرد یکسان را مقایسه کرد. در اینجا ما مقایسه می کنیم:
-
یک رویکرد درک لیست
-
بوها
map()
با رویکرد لامبدا
با استفاده از توابع Lambda ، می توانیم هنگام زمان بندی آنها ، داده های موجود را به توابع خود منتقل کنیم. این مستقیماً سناریوهای دنیای واقعی را که عملکردهای شما با داده های موجود کار می کنند ، اندازه گیری می کند. محاسبه نسبت باعث می شود درک دقیق یک رویکرد سریعتر از روش دیگر آسان شود.
در این حالت ، می توانیم ببینیم که درک لیست حدود 2.17 برابر سریعتر از رویکرد نقشه برای این عملیات خاص است.
در cProfile
ماژول
cProfile
پروفیلر مبتنی بر C پایتون است که آمار مفصلی در مورد تماس های عملکرد ارائه می دهد. بسیاری از توسعه دهندگان از آن با تنظیمات پیش فرض خود استفاده می کنند:
import cProfile
def my_function():
total = 0
for i in range(100000): # Reduced for faster execution
total += i
return total
cProfile.run('my_function()')
# Sample output:
# 4 function calls in 0.002 seconds
#
# Ordered by: standard name
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.000 0.000 0.002 0.002 <string>:1(<module>)
# 1 0.002 0.002 0.002 0.002 <stdin>:1(my_function)
# 1 0.000 0.000 0.002 0.002 {built-in method builtins.exec}
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
این مثال اساسی پروفایل را اجرا می کند روی یک عملکرد ساده که اعداد را از 0 تا 99.999 خلاصه می کند. خروجی چندین بخش اصلی اطلاعات را ارائه می دهد:
-
ncalls
: چند بار هر عملکرد نامیده می شد -
tottime
: کل زمان صرف شده در عملکرد (به استثنای زمان در زیرمجموعه ها) -
percall
: زمان در هر تماس (tottime
تقسیم شده توسطncalls
) -
cumtime
: زمان تجمعی که در این عملکرد و کلیه عملکردهای فرعی صرف شده است -
filename:lineno(function)
: جایی که عملکرد تعریف شده است
این یک دیدگاه جامع در مورد زمان صرف زمان در کد شما به شما می دهد ، اما کارهای بیشتری وجود دارد که می توانید با آنها انجام دهید cProfile
بشر
قدرت واقعی از تکنیک های پیشرفته استفاده ناشی می شود:
مرتب سازی نتایج:
import cProfile
import pstats
# Profile the function
profiler = cProfile.Profile()
profiler.enable()
my_function()
profiler.disable()
# Create stats object
stats = pstats.Stats(profiler)
# Sort by different metrics
stats.sort_stats('cumulative').print_stats(10) # Top 10 functions by cumulative time
stats.sort_stats('calls').print_stats(10) # Top 10 functions by call count
stats.sort_stats('time').print_stats(10) # Top 10 functions by time
# Sample output for cumulative sorting:
# 2 function calls in 0.002 seconds
#
# Ordered by: cumulative time
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.002 0.002 0.002 0.002 <stdin>:1(my_function)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
این مثال روش کنترل پروفایل را نشان می دهد process و نتایج را به روش های مختلف مرتب کنید. مزایای آن عبارتند از:
-
می توانید پروفایل را در بخش های خاص کد فعال یا غیرفعال کنید
-
برای شناسایی انواع مختلف تنگناها می توانید نتایج را بر اساس معیارهای مختلف مرتب کنید:
-
cumulative
: کارکردهایی را پیدا کنید که بیشترین زمان را به طور کلی مصرف می کنند (از جمله زیرمجموعه ها) -
calls
: توابعی را که بیشتر به نام آنها پیدا کنید پیدا کنید -
time
: توابع خود را با بالاترین زمان خود پیدا کنید (به استثنای زیرمجموعه ها)
-
-
بازده را فقط به نتایج برتر N محدود کنید
print_stats(N)
این انعطاف پذیری به شما امکان تمرکز می دهد روی جنبه های عملکرد خاص کد شما.
نتایج فیلتر:
stats.strip_dirs().print_stats() # Remove directory paths for cleaner output
stats.print_stats('my_module') # Only show results from my_module
# Sample output with strip_dirs():
# 2 function calls in 0.002 seconds
#
# Random listing order was used
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.002 0.002 0.002 0.002 <stdin>:1(my_function)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
این تکنیک های فیلتر هنگام کار با برنامه های بزرگتر بسیار ارزشمند هستند:
-
strip_dirs()
مسیرهای دایرکتوری را حذف می کند و خروجی را بسیار خواندنی می کند -
print_stats('my_module')
نتایج فیلترها فقط توابع را از یک ماژول خاص نشان می دهد ، به شما امکان تمرکز می دهد روی کد شما به جای کد کتابخانه
این امر به ویژه در هنگام پروفایل برنامه های بزرگ که در آن خروجی کامل ممکن است صدها یا هزاران تماس عملکردی باشد ، مفید است.
در pstats
ماژول
در pstats
ماژول اغلب نادیده گرفته می شود اما روشهای قدرتمندی برای تجزیه و تحلیل داده های پروفایل فراهم می کند:
ذخیره و بارگیری داده های پروفایل:
import cProfile
import pstats
# Save profile data to a file
cProfile.run('my_function()', 'my_profile.stats')
# Load and analyze later
stats = pstats.Stats('my_profile.stats')
stats.strip_dirs().sort_stats('cumulative').print_stats(10)
# Sample output:
# Wed Mar 20 14:30:00 1403 my_profile.stats
#
# 4 function calls in 0.002 seconds
#
# Ordered by: cumulative time
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.000 0.000 0.002 0.002 {built-in method builtins.exec}
# 1 0.000 0.000 0.002 0.002 <string>:1(<module>)
# 1 0.002 0.002 0.002 0.002 <stdin>:1(my_function)
# 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
این مثال روش ذخیره داده های پروفایل را در یک پرونده نشان می دهد و بعداً آن را برای تجزیه و تحلیل بارگیری می کند. مزایای اصلی:
-
می توانید داده های پروفایل را در یک جلسه یا محیط جمع آوری کرده و آن را در دیگری تجزیه و تحلیل کنید
-
شما می توانید داده های پروفایل را با اعضای تیم به اشتراک بگذارید بدون اینکه آنها نیاز به اجرای کد داشته باشند
-
شما می توانید داده های پروفایل را از محیط های تولید ذخیره کنید که در آن تجزیه و تحلیل تعاملی امکان پذیر نباشد
-
برای پیگیری بهبود عملکرد می توانید اجرای مختلف را با گذشت زمان مقایسه کنید
این رویکرد جمع آوری داده ها را از تجزیه و تحلیل جدا می کند و آن را برای برنامه های دنیای واقعی انعطاف پذیر می کند.
ترکیب چندین پروفایل:
stats = pstats.Stats('profile1.stats')
stats.add('profile2.stats')
stats.add('profile3.stats')
stats.sort_stats('time').print_stats()
# This allows you to combine results from multiple profiling runs,
# useful for aggregating data from different test cases or scenarios
این ویژگی قدرتمند به شما امکان می دهد نتایج حاصل از اجرای چندین پروفایل را ترکیب کنید. این برای:
-
مقایسه عملکرد در ورودی های مختلف
-
جمع آوری داده ها از چندین سناریو آزمون
-
ترکیب داده ها از قسمت های مختلف برنامه شما
-
ساختن یک تصویر جامع تر در چندین اجرا
با ترکیب آمار از چندین اجرا ، می توانید الگوهای را شناسایی کنید که ممکن است از یک جلسه پروفایل واحد آشکار نباشد.
در profile
ماژول
در profile
ماژول یک اجرای خالص پایتون از رابط پروفایل است. در حالی که کندتر از cProfile
، می تواند برای موارد خاص انعطاف پذیر تر باشد:
import profile
def my_function():
total = 0
for i in range(100000): # Using 100000 for faster execution
total += i
return total
profile.run('my_function()')
# Sample output:
# 5 function calls in 0.011 seconds
#
# Ordered by: standard name
#
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.000 0.000 0.002 0.002 :0(exec)
# 1 0.009 0.009 0.009 0.009 :0(setprofile)
# 1 0.000 0.000 0.002 0.002 <string>:1(<module>)
# 1 0.000 0.000 0.011 0.011 profile:0(my_function())
# 0 0.000 0.000 profile:0(profiler)
# 1 0.002 0.002 0.002 0.002 <stdin>:1(my_function)
در profile
ماژول به طور مشابه کار می کند cProfile
اما مزایایی در سناریوهای خاص ارائه می دهد:
-
این در پایتون خالص اجرا شده است ، در صورت نیاز به رفتار پروفایل سفارشی ، اصلاح آن آسان تر می شود
-
شما می توانید زیر کلاس و گسترش آن برای اجرای منطق پروفایل سفارشی
-
این امکان کنترل ریز و درشت تر بر پروفایل را فراهم می کند process
-
برای پروفایل در محیط هایی که ممکن است پسوند C در دسترس نباشد مفید است
در حالی که کندتر از cProfile
(از آنجا که به جای C در پایتون اجرا شده است) ، انعطاف پذیری آن باعث می شود که نیازهای تخصصی پروفایل را ارزشمند کند.
در profile
ماژول همان API را دنبال می کند cProfile
، بنابراین می توانید از تمام تکنیک های یکسان برای تجزیه و تحلیل نتایج استفاده کنید.
آزمایشات عملی
بیایید این ابزارها را با برخی از آزمایشات استفاده کنیم.
راه اندازی
ابتدا یک ماژول ساده پایتون با توابع مختلف برای پروفایل ایجاد کنید:
# profiling_example.py
import time
import random
def process_data(data):
result = []
for item in data:
result.append(process_item(item))
return result
def process_item(item):
# Simulate processing time
time.sleep(0.0001) # Small delay for demonstration purposes
return item * 2
def generate_data(size):
return [random.randint(1, 100) for _ in range(size)]
def process_data_optimized(data):
return [process_item(item) for item in data]
def main():
data = generate_data(50)
result1 = process_data(data)
result2 = process_data_optimized(data)
assert result1 == result2
return result1
if __name__ == "__main__":
main()
آزمایش 1: Basic vs Advanced timeit
استفاده
بیایید روشهای مختلف زمان بندی توابع خود را مقایسه کنیم:
import timeit
from profiling_example import generate_data, process_data, process_data_optimized
# Method 1: Basic string evaluation (limited but simple)
setup1 = """
from profiling_example import generate_data, process_data
data = generate_data(5) # Using a small size for demonstration
"""
basic_time = timeit.timeit('process_data(data)', setup=setup1, number=5)
print(f"Basic timing: {basic_time:.4f} seconds")
# Method 2: Using lambda for better control
data = generate_data(5)
advanced_time = timeit.timeit(lambda: process_data(data), number=5)
print(f"Advanced timing: {advanced_time:.4f} seconds")
# Method 3: Comparing implementations
data = generate_data(5)
original_time = timeit.timeit(lambda: process_data(data), number=5)
optimized_time = timeit.timeit(lambda: process_data_optimized(data), number=5)
print(f"Original implementation: {original_time:.4f} seconds")
print(f"Optimized implementation: {optimized_time:.4f} seconds")
print(f"Improvement ratio: {original_time/optimized_time:.2f}x")
# Sample output:
# Basic timing: 0.0032 seconds
# Advanced timing: 0.0034 seconds
# Original implementation: 0.0033 seconds
# Optimized implementation: 0.0034 seconds
# Improvement ratio: 0.98x
این آزمایش سه رویکرد مختلف برای کد زمان بندی با timeit
ماژول:
روش 1: ارزیابی رشته اساسی – این رویکرد پس از اجرای کد تنظیم ، رشته کد را ارزیابی می کند. مزایای آن عبارتند از:
-
نحو ساده برای نیازهای اساسی زمان بندی
-
کد تنظیم فقط یک بار اجرا می شود ، نه در هر زمان اجرا
-
مناسب برای زمان بندی عبارات ساده
روش 2: توابع لامبدا – این رویکرد پیشرفته تر از توابع Lambda استفاده می کند تا توابع ما را مستقیماً فراخوانی کند. مزایا شامل:
-
دسترسی مستقیم به توابع و متغیرها در دامنه فعلی
-
نیازی به import توابع در کد تنظیم
-
برای عملکردهای زمان بندی که استدلال می کنند بهتر است
-
شهودی تر برای سناریوهای زمان بندی پیچیده
روش 3: مقایسه اجرای – این رویکرد عملی دو پیاده سازی متفاوت از همان عملکرد را مقایسه می کند. این ارزشمند است وقتی:
-
تصمیم گیری بین پیاده سازی های جایگزین
-
اندازه گیری تأثیر بهینه سازی
-
تعیین تفاوت عملکرد با نسبت
در این مثال ، درک لیست به طور قابل توجهی سریعتر نیست زیرا هزینه غالب این است time.sleep()
در هر دو پیاده سازی تماس بگیرید. در موارد دنیای واقعی با محاسبات واقعی به جای خواب ، تفاوت اغلب برجسته تر است.
آزمایش 2: مؤثر cProfile
تجزیه
حالا بیایید استفاده کنیم cProfile
برای شناسایی تنگناها:
import cProfile
import pstats
import io
from profiling_example import main
# Method 1: Basic profiling
cProfile.run('main()')
# Sample output snippet:
# 679 function calls in 0.014 seconds
# ncalls tottime percall cumtime percall filename:lineno(function)
# 100 0.000 0.000 0.014 0.000 profiling_example.py:10(process_item)
# 100 0.014 0.000 0.014 0.000 {built-in method time.sleep}
# 1 0.000 0.000 0.007 0.007 profiling_example.py:4(process_data)
# 1 0.000 0.000 0.007 0.007 profiling_example.py:18(process_data_optimized)
# Method 2: Capturing and analyzing results
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()
# Redirect output to string for analysis
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
stats.print_stats(10) # Print top 10 functions by cumulative time
print(s.getvalue())
# Method 3: Focus روی specific functions
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
stats.print_callers('process_item') # Show what's calling this function
print(s.getvalue())
# Sample output for the callers analysis:
# Function was called by...
# ncalls tottime cumtime
# profiling_example.py:10(process_item) <-
# 50 0.000 0.007 profiling_example.py:4(process_data)
# 50 0.000 0.007 profiling_example.py:18(process_data_optimized)
این آزمایش سه قدرتمند را نشان می دهد cProfile
تکنیک های شناسایی تنگناها:
روش 1: پروفایل اساسی – با استفاده از cProfile.run()
برای پروفایل یک تماس عملکرد ، یک مرور کلی از عملکرد را ارائه می دهد. این تکنیک:
-
عکس فوری سریع به شما می دهد
-
دقیقاً نشان می دهد که زمان صرف می شود
-
استفاده از حداقل تنظیمات آسان است
-
بیشترین عملیات را مشخص می کند
به عنوان مثال ، ما بلافاصله می توانیم آن را ببینیم time.sleep()
بیشتر زمان اعدام را مصرف می کند.
روش 2: پروفایل و تجزیه و تحلیل برنامه ای – این رویکرد کنترل بیشتری به شما می دهد:
-
برای بخش های خاص کد می توانید پروفایل را فعال یا غیرفعال کنید
-
برای تجزیه و تحلیل بیشتر می توانید نتایج را در یک متغیر ذخیره کنید
-
شما می توانید روش طبقه بندی و نمایش نتایج را سفارشی کنید
-
می توانید خروجی را به یک رشته یا پرونده برای پردازش پس از آن هدایت کنید
این روش به ویژه برای پروفایل بخش های خاص یک برنامه بزرگتر مفید است.
روش 3: تجزیه و تحلیل تماس گیرنده – print_callers()
روش بسیار ارزشمند است زیرا:
-
نشان می دهد کدام یک از توابع شما را به توابع تنگنای شما می گویند
-
به شناسایی مسیرهای کد کمک می کند تا در مسائل عملکردی نقش داشته باشند
-
نشان می دهد که چند بار هر تماس گیرنده یک عملکرد خاص را فراخوانی می کند
-
زمینه ای را فراهم می کند که برای درک الگوهای عملکرد بسیار مهم است
در مثال ما می توانیم ببینیم که هر دو process_data
وت process_data_optimized
تماس می گیرند process_item
هر کدام 50 بار ، تأیید می کنند که آنها به طور مساوی در تنگناها نقش دارند.
این فوری به ما نشان می دهد که process_item
تنگنا است ، به طور خاص time.sleep()
در داخل آن تماس بگیرید ، و هر دو پیاده سازی آن را به همان اندازه خوانده می شود.
آزمایش 3: ترکیب ابزار برای پروفایل در دنیای واقعی
در سناریوهای دنیای واقعی ، ترکیب ابزارهای پروفایل کامل ترین تصویر را ارائه می دهد:
import cProfile
import pstats
import timeit
from profiling_example import main, process_data, process_data_optimized, generate_data
# First, use timeit to get baseline performance of main components
data = generate_data(50)
time_process = timeit.timeit(lambda: process_data(data), number=3)
time_process_opt = timeit.timeit(lambda: process_data_optimized(data), number=3)
print(f"Process data: {time_process:.4f}s")
print(f"Process data optimized: {time_process_opt:.4f}s")
# Sample output:
# Process data: 0.0196s
# Process data optimized: 0.0194s
# Then, use cProfile for deeper insights
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()
# Save stats for later analysis
profiler.dump_stats('profile_results.stats')
# Load and analyze
stats = pstats.Stats('profile_results.stats')
stats.strip_dirs().sort_stats('cumulative').print_stats(10)
# Sample output:
# Wed Mar 20 14:30:00 1403 profile_results.stats
# 659 function calls in 0.013 seconds
# Ordered by: cumulative time
# ncalls tottime percall cumtime percall filename:lineno(function)
# 1 0.000 0.000 0.013 0.013 profiling_example.py:21(main)
# 100 0.000 0.000 0.013 0.000 profiling_example.py:10(process_item)
# 100 0.013 0.000 0.013 0.000 {built-in method time.sleep}
این آزمایش یک استراتژی جامع پروفایل در دنیای واقعی را نشان می دهد که چندین ابزار را ترک می کند:
مرحله اول: زمان بندی سطح بالا با timeit
– ما شروع می کنیم timeit
به:
-
معیارهای عملکرد پایه را برای عملکردهای خاص دریافت کنید
-
پیاده سازی های مختلف را مستقیماً مقایسه کنید
-
زمان اجرای کلی را اندازه گیری کنید
-
مشخص کنید که کدام مؤلفه های سطح بالا ممکن است به بهینه سازی نیاز داشته باشند
این یک بررسی سریع به ما می دهد که هر دو پیاده سازی تقریباً در همان زمان انجام می دهند و یافته های قبلی ما را تأیید می کنند.
مرحله دوم: پروفایل دقیق با cProfile
– بعد ، ما استفاده می کنیم cProfile
به:
-
یک عملکرد با عملکرد را از زمان اجرا دریافت کنید
-
تنگناهای خاص را در سطح گرانول شناسایی کنید
-
تعداد تماس های هر عملکرد را مشاهده کنید
-
سلسله مراتب تماس را درک کنید
مرحله سوم: صرفه جویی و تجزیه و تحلیل با pstats
– سرانجام ، ما:
-
برای پایداری داده های پروفایل را در یک پرونده ذخیره کنید
-
داده ها را بارگیری کرده و فیلتر/مرتب سازی را اعمال کنید
-
تمرکز روی بیشترین عملکردها
-
یک خروجی تمیز و قابل خواندن دریافت کنید
این رویکرد چند منظوره چندین مزیت را ارائه می دهد:
-
شما هر دو بینش سطح بالا و مفصلی دریافت می کنید
-
برای مقایسه بعدی می توانید داده های پروفایل را ذخیره کنید
-
می توانید نتایج را با اعضای تیم به اشتراک بگذارید
-
می توانید تغییرات عملکرد را با گذشت زمان ردیابی کنید
در مثال ما ، ما تأیید می کنیم که تنگنای اصلی ما این است time.sleep()
در داخل تماس بگیرید process_item
، که بیشتر زمان اعدام را به خود اختصاص می دهد. بدون این رویکرد ترکیبی ، ما ممکن است جزئیات مهم را از دست داده یا زمان را هدر داده ایم تا قسمت های اشتباه کد خود را بهینه کنیم.
این رویکرد هر دو اطلاعات زمان بندی سطح بالا و داده های پروفایل دقیق را به شما می دهد ، و امکان تجزیه و تحلیل جامع عملکرد را فراهم می کند.
بهترین روشها
مستقر روی آزمایشات ما ، در اینجا بهترین روشها برای پروفایل مؤثر است:
-
با ابزار مناسب برای کار شروع کنید:
-
استفاده کردن
timeit
برای اندازه گیری سریع و هدفمند توابع خاص یا بلوک های کد -
استفاده کردن
cProfile
برای تجزیه و تحلیل برنامه جامع هنگامی که شما باید درک کنید که چگونه تمام قسمت های کد شما در تعامل است -
استفاده کردن
pstats
برای تجزیه و تحلیل عمیق داده های پروفایل هنگامی که شما نیاز به فیلتر ، مرتب سازی و تفسیر نتایج پروفایل پیچیده دارید
-
به عنوان مثال ، اگر فقط سعی می کنید بین دو اجرای یک الگوریتم مرتب سازی تصمیم بگیرید ، timeit
کافی است اما اگر می خواهید درک کنید که چرا کل برنامه وب شما کند است ، با شروع کنید cProfile
بشر
-
بار کاری واقع بینانه:
-
معیارهای مصنوعی اغلب گمراه می شوند زیرا آنها الگوهای استفاده در دنیای واقعی را منعکس نمی کنند
-
از اندازه داده های شبیه به تولید استفاده کنید تا ببینید که چگونه مقیاس کد شما با ورودی های واقع گرایانه است
-
چندین تکرار را اجرا کنید تا واریانس حساب کنید و اطمینان حاصل کنید که نتایج شما قابل اعتماد است
-
عملکردی که با 10 مورد سریع باشد ممکن است با 10،000 درد بسیار دردناک باشد. همیشه با اندازه داده هایی که مطابق با نیازهای تولید شما است ، تست کنید.
-
تمرکز روی معیارهای مناسب:
-
cumulative
زمان کل زمان صرف شده در یک عملکرد و تمام تماس های آن را نشان می دهد. برای یافتن گرانترین عملیات مفید است. -
tottime
زمان صرف شده فقط در خود عملکرد را نشان می دهد. برای یافتن پیاده سازی های ناکارآمد مفید است. -
ncalls
به شناسایی عملکردهای بیش از حد کمک می کند. برای یافتن عملیات اضافی مفید است.
-
به عنوان مثال ، عملکردی با یک کوچک tottime
اما بزرگ cumulative
ممکن است زمان خود کارآمد باشد اما به زیر عملکردهای گران قیمت می پردازد.
-
برای مقایسه داده های پروفایل را ذخیره کنید:
-
استفاده کردن
profiler.dump_stats()
برای ذخیره داده ها از نسخه های مختلف کد خود -
قبل و بعد از بهینه سازی را مقایسه کنید تا پیشرفت ها را تعیین کنید
-
عملکرد را به مرور زمان دنبال کنید تا زودرس رگرسیون را بدست آورید
-
این عمل به شما کمک می کند تا ثابت کنید که بهینه سازی های شما در واقع کار می کنند و مانع از تحقیر عملکرد با گذشت زمان می شود.
-
به دنبال قانون 80/20 باشید:
-
80 ٪ از زمان اغلب در 20 ٪ کد صرف می شود. تلاش های بهینه سازی را متمرکز کنید روی این “نقاط داغ”
-
تلاش های بهینه سازی را متمرکز کنید روی توابع با بالاترین زمان تجمعی.
-
آنچه را که کند نیست بهینه نکنید – زمان بهینه سازی زودرس را پس انداز کنید و می تواند کد را پیچیده تر کند.
-
به عنوان مثال ، در آزمایشات ما ، time.sleep()
تماس تنگنا روشن بود. بهینه سازی هر چیز دیگری تا زمانی که مورد توجه قرار نگیرد بی معنی خواهد بود.
با پیروی از این شیوه ها ، کارآمدترین استفاده از ابزارهای پروفایل خود را انجام می دهید و تلاش های بهینه سازی خود را در جایی که آنها بیشترین تأثیر را دارند ، متمرکز می کنید.
پایان
ابزارهای پروفایل داخلی پایتون یک زرادخانه قدرتمند را برای شناسایی و حل تنگناهای عملکرد در کد شما ارائه می دهند. با استفاده از timeit
با cProfile
وت pstats
ماژول ها به طور مؤثر ، می توانید بدون تکیه به عملکرد برنامه خود بینش عمیقی کسب کنید روی ابزارهای شخص ثالث.
هر ابزار یک هدف خاص را ارائه می دهد:
-
timeit
به شما در اندازه گیری زمان اجرای قطعه های کد خاص کمک می کند -
cProfile
نمای کاملی از تماس های عملکردی و زمان اجرا به شما می دهد -
pstats
به شما امکان می دهد داده های پروفایل را تجزیه و تحلیل ، فیلتر و تفسیر کنید -
profile
یک رابط پروفایل قابل تنظیم برای موارد خاص فراهم می کند
همانطور که در آزمایش های عملی خود نشان دادیم ، بزرگترین قدرت از ترکیب این ابزارها ناشی می شود. این به شما امکان می دهد به بهینه سازی عملکرد به طور سیستماتیک نزدیک شوید:
-
نگرانی های عملکرد سطح بالا را با
timeit
-
در تنگناهای خاص با آن حفر کنید
cProfile
-
تجزیه و تحلیل و تفسیر نتایج با
pstats
-
بهینه سازی های هدفمند را مبتنی بر کنید روی داده ها ، نه حدس زدن
به یاد داشته باشید که پروفایل به همان اندازه یک هنر است که یک علم است. هدف فقط سریعتر کردن کد نیست بلکه درک این مسئله است که چرا در وهله اول کند است. با تکنیک های نشان داده شده در این مقاله ، شما برای مقابله با چالش های عملکرد در برنامه های پایتون خود مجهز هستید.
این تکنیک های پروفایل را روی کد خود اعمال کنید و از آنچه کشف می کنید تعجب خواهید کرد. غالباً ، تنگناها جایی نیستند که انتظار دارید آنها باشند!
منابع و خواندن بیشتر
-
پیتون
timeit
مستندات: مستندات رسمی برایtimeit
ماژول ، با توضیحات مفصل در مورد همه پارامترها و توابع. -
پیتون
cProfile
مستندات: راهنمای جامع برای پروفایل ماژول ها ، از جمله هر دوcProfile
وتprofile
بشر -
پیتون
pstats
مستندات: مرجع مفصل برایpstats
ماژول ، توضیح کلیه روشهای تجزیه و تحلیل داده های پروفایل. -
Python Profilers – رسمی Documentation: مستندات رسمی کامل روی قابلیت های پروفایل پایتون.
-
Python Speed - نکات عملکرد: مجموعه ای از نکات عملی برای بهینه سازی کد پایتون پس از شناسایی تنگناها.
-
Python Profilers: توضیح عمیق در مورد پروفایل در پایتون ، از جمله جزئیات روی سربار و دقت.
-
تکنیک های بهینه سازی جنگو: مشاوره عملی روی بهینه سازی کد Django ORM در برنامه های دنیای واقعی.
-
روشهای جادویی پایتون چگونه کار می کنند: یک راهنمای عملی: مقاله قبلی من روی FreecodeCamp پوشش عمیق شیرجه روی روشهای جادویی پایتون.
منتشر شده در 1404-03-25 23:36:09