Python به دلیل سادگی و خوانایی شناخته شده است و آن را در بین توسعه دهندگان مورد علاقه قرار می دهد. اما این سادگی گاهی به هزینه عملکرد می رسد. هنگامی که برنامه Python شما رشد می کند یا نیاز به تحمل بار کاری بزرگتر دارد ، درک آنچه در زیر کاپوت اتفاق می افتد بسیار مهم است.

در حالی که بسیاری از توسعه دهندگان به ابزارهای پروفایل شخص ثالث می رسند ، کتابخانه استاندارد پایتون در حال حاضر با قابلیت های پروفایل قدرتمندی که غالباً مورد غفلت یا کمبود قرار می گیرند ، بسته بندی شده است.

در این مقاله ، شما یاد می گیرید که چگونه از این ابزارهای پروفایل داخلی فراتر از استفاده اساسی آنها استفاده کنید. شما می توانید روش ترکیب و استفاده از آنها را برای به دست آوردن بینش عمیق در مورد عملکرد کد خود بدون نصب بسته های اضافی کشف کنید.

فهرست مطالب

  • پیش نیازهای

  • پروفایل داخلی زرادخانه

    • در timeit ماژول

    • در cProfile ماژول

    • در pstats ماژول

    • در profile ماژول

  • آزمایشات عملی

    • راه اندازی

    • آزمایش 1: Basic vs Advanced timeit استفاده

    • آزمایش 2: مؤثر cProfile تجزیه

    • آزمایش 3: ترکیب ابزار برای پروفایل در دنیای واقعی

  • بهترین روشها

  • پایان

  • منابع و خواندن بیشتر

پیش نیازهای

قبل از غواصی به تکنیک های پروفایل ، حتماً:

  1. پایتون 3.6+: تمام نمونه های این مقاله با نسخه های Python 3.6 و جدیدتر سازگار است.

  2. دانش اساسی پایتون: شما باید با اصول پایتون مانند توابع ، ماژول ها و ساختار داده های اساسی راحت باشید.

  3. یک محیط آزمایش: یا یک محیط محلی پایتون یا یک محیط مجازی که در آن می توانید نمونه های کد را اجرا کنید.

برای این آموزش هیچ کتابخانه خارجی لازم نیست زیرا ما به طور انحصاری تمرکز خواهیم کرد روی ابزارهای پروفایل داخلی پایتون. می توانید نسخه 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

این مثال نشان می دهد که چگونه می توان دو اجرای متفاوت از عملکرد یکسان را مقایسه کرد. در اینجا ما مقایسه می کنیم:

  1. یک رویکرد درک لیست

  2. بوها 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 و نتایج را به روش های مختلف مرتب کنید. مزایای آن عبارتند از:

  1. می توانید پروفایل را در بخش های خاص کد فعال یا غیرفعال کنید

  2. برای شناسایی انواع مختلف تنگناها می توانید نتایج را بر اساس معیارهای مختلف مرتب کنید:

    • cumulative: کارکردهایی را پیدا کنید که بیشترین زمان را به طور کلی مصرف می کنند (از جمله زیرمجموعه ها)

    • calls: توابعی را که بیشتر به نام آنها پیدا کنید پیدا کنید

    • time: توابع خود را با بالاترین زمان خود پیدا کنید (به استثنای زیرمجموعه ها)

  3. بازده را فقط به نتایج برتر 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}

این مثال روش ذخیره داده های پروفایل را در یک پرونده نشان می دهد و بعداً آن را برای تجزیه و تحلیل بارگیری می کند. مزایای اصلی:

  1. می توانید داده های پروفایل را در یک جلسه یا محیط جمع آوری کرده و آن را در دیگری تجزیه و تحلیل کنید

  2. شما می توانید داده های پروفایل را با اعضای تیم به اشتراک بگذارید بدون اینکه آنها نیاز به اجرای کد داشته باشند

  3. شما می توانید داده های پروفایل را از محیط های تولید ذخیره کنید که در آن تجزیه و تحلیل تعاملی امکان پذیر نباشد

  4. برای پیگیری بهبود عملکرد می توانید اجرای مختلف را با گذشت زمان مقایسه کنید

پیشنهاد می‌کنیم بخوانید:  آرگومان های با طول متغیر در پایتون با *args و **kwargs

این رویکرد جمع آوری داده ها را از تجزیه و تحلیل جدا می کند و آن را برای برنامه های دنیای واقعی انعطاف پذیر می کند.

ترکیب چندین پروفایل:

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

این ویژگی قدرتمند به شما امکان می دهد نتایج حاصل از اجرای چندین پروفایل را ترکیب کنید. این برای:

  1. مقایسه عملکرد در ورودی های مختلف

  2. جمع آوری داده ها از چندین سناریو آزمون

  3. ترکیب داده ها از قسمت های مختلف برنامه شما

  4. ساختن یک تصویر جامع تر در چندین اجرا

با ترکیب آمار از چندین اجرا ، می توانید الگوهای را شناسایی کنید که ممکن است از یک جلسه پروفایل واحد آشکار نباشد.

در 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 اما مزایایی در سناریوهای خاص ارائه می دهد:

  1. این در پایتون خالص اجرا شده است ، در صورت نیاز به رفتار پروفایل سفارشی ، اصلاح آن آسان تر می شود

  2. شما می توانید زیر کلاس و گسترش آن برای اجرای منطق پروفایل سفارشی

  3. این امکان کنترل ریز و درشت تر بر پروفایل را فراهم می کند process

  4. برای پروفایل در محیط هایی که ممکن است پسوند 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 – سرانجام ، ما:

  • برای پایداری داده های پروفایل را در یک پرونده ذخیره کنید

  • داده ها را بارگیری کرده و فیلتر/مرتب سازی را اعمال کنید

  • تمرکز روی بیشترین عملکردها

  • یک خروجی تمیز و قابل خواندن دریافت کنید

این رویکرد چند منظوره چندین مزیت را ارائه می دهد:

  1. شما هر دو بینش سطح بالا و مفصلی دریافت می کنید

  2. برای مقایسه بعدی می توانید داده های پروفایل را ذخیره کنید

  3. می توانید نتایج را با اعضای تیم به اشتراک بگذارید

  4. می توانید تغییرات عملکرد را با گذشت زمان ردیابی کنید

در مثال ما ، ما تأیید می کنیم که تنگنای اصلی ما این است time.sleep() در داخل تماس بگیرید process_item، که بیشتر زمان اعدام را به خود اختصاص می دهد. بدون این رویکرد ترکیبی ، ما ممکن است جزئیات مهم را از دست داده یا زمان را هدر داده ایم تا قسمت های اشتباه کد خود را بهینه کنیم.

این رویکرد هر دو اطلاعات زمان بندی سطح بالا و داده های پروفایل دقیق را به شما می دهد ، و امکان تجزیه و تحلیل جامع عملکرد را فراهم می کند.

بهترین روشها

مستقر روی آزمایشات ما ، در اینجا بهترین روشها برای پروفایل مؤثر است:

  1. با ابزار مناسب برای کار شروع کنید:

    • استفاده کردن timeit برای اندازه گیری سریع و هدفمند توابع خاص یا بلوک های کد

    • استفاده کردن cProfile برای تجزیه و تحلیل برنامه جامع هنگامی که شما باید درک کنید که چگونه تمام قسمت های کد شما در تعامل است

    • استفاده کردن pstats برای تجزیه و تحلیل عمیق داده های پروفایل هنگامی که شما نیاز به فیلتر ، مرتب سازی و تفسیر نتایج پروفایل پیچیده دارید

به عنوان مثال ، اگر فقط سعی می کنید بین دو اجرای یک الگوریتم مرتب سازی تصمیم بگیرید ، timeit کافی است اما اگر می خواهید درک کنید که چرا کل برنامه وب شما کند است ، با شروع کنید cProfileبشر

  1. بار کاری واقع بینانه:

    • معیارهای مصنوعی اغلب گمراه می شوند زیرا آنها الگوهای استفاده در دنیای واقعی را منعکس نمی کنند

    • از اندازه داده های شبیه به تولید استفاده کنید تا ببینید که چگونه مقیاس کد شما با ورودی های واقع گرایانه است

    • چندین تکرار را اجرا کنید تا واریانس حساب کنید و اطمینان حاصل کنید که نتایج شما قابل اعتماد است

عملکردی که با 10 مورد سریع باشد ممکن است با 10،000 درد بسیار دردناک باشد. همیشه با اندازه داده هایی که مطابق با نیازهای تولید شما است ، تست کنید.

  1. تمرکز روی معیارهای مناسب:

    • cumulative زمان کل زمان صرف شده در یک عملکرد و تمام تماس های آن را نشان می دهد. برای یافتن گرانترین عملیات مفید است.

    • tottime زمان صرف شده فقط در خود عملکرد را نشان می دهد. برای یافتن پیاده سازی های ناکارآمد مفید است.

    • ncalls به شناسایی عملکردهای بیش از حد کمک می کند. برای یافتن عملیات اضافی مفید است.

به عنوان مثال ، عملکردی با یک کوچک tottime اما بزرگ cumulative ممکن است زمان خود کارآمد باشد اما به زیر عملکردهای گران قیمت می پردازد.

  1. برای مقایسه داده های پروفایل را ذخیره کنید:

    • استفاده کردن profiler.dump_stats() برای ذخیره داده ها از نسخه های مختلف کد خود

    • قبل و بعد از بهینه سازی را مقایسه کنید تا پیشرفت ها را تعیین کنید

    • عملکرد را به مرور زمان دنبال کنید تا زودرس رگرسیون را بدست آورید

این عمل به شما کمک می کند تا ثابت کنید که بهینه سازی های شما در واقع کار می کنند و مانع از تحقیر عملکرد با گذشت زمان می شود.

  1. به دنبال قانون 80/20 باشید:

    • 80 ٪ از زمان اغلب در 20 ٪ کد صرف می شود. تلاش های بهینه سازی را متمرکز کنید روی این “نقاط داغ”

    • تلاش های بهینه سازی را متمرکز کنید روی توابع با بالاترین زمان تجمعی.

    • آنچه را که کند نیست بهینه نکنید – زمان بهینه سازی زودرس را پس انداز کنید و می تواند کد را پیچیده تر کند.

به عنوان مثال ، در آزمایشات ما ، time.sleep() تماس تنگنا روشن بود. بهینه سازی هر چیز دیگری تا زمانی که مورد توجه قرار نگیرد بی معنی خواهد بود.

با پیروی از این شیوه ها ، کارآمدترین استفاده از ابزارهای پروفایل خود را انجام می دهید و تلاش های بهینه سازی خود را در جایی که آنها بیشترین تأثیر را دارند ، متمرکز می کنید.

پایان

ابزارهای پروفایل داخلی پایتون یک زرادخانه قدرتمند را برای شناسایی و حل تنگناهای عملکرد در کد شما ارائه می دهند. با استفاده از timeitبا cProfileوت pstats ماژول ها به طور مؤثر ، می توانید بدون تکیه به عملکرد برنامه خود بینش عمیقی کسب کنید روی ابزارهای شخص ثالث.

هر ابزار یک هدف خاص را ارائه می دهد:

  • timeit به شما در اندازه گیری زمان اجرای قطعه های کد خاص کمک می کند

  • cProfile نمای کاملی از تماس های عملکردی و زمان اجرا به شما می دهد

  • pstats به شما امکان می دهد داده های پروفایل را تجزیه و تحلیل ، فیلتر و تفسیر کنید

  • profile یک رابط پروفایل قابل تنظیم برای موارد خاص فراهم می کند

همانطور که در آزمایش های عملی خود نشان دادیم ، بزرگترین قدرت از ترکیب این ابزارها ناشی می شود. این به شما امکان می دهد به بهینه سازی عملکرد به طور سیستماتیک نزدیک شوید:

  1. نگرانی های عملکرد سطح بالا را با timeit

  2. در تنگناهای خاص با آن حفر کنید cProfile

  3. تجزیه و تحلیل و تفسیر نتایج با pstats

  4. بهینه سازی های هدفمند را مبتنی بر کنید روی داده ها ، نه حدس زدن

به یاد داشته باشید که پروفایل به همان اندازه یک هنر است که یک علم است. هدف فقط سریعتر کردن کد نیست بلکه درک این مسئله است که چرا در وهله اول کند است. با تکنیک های نشان داده شده در این مقاله ، شما برای مقابله با چالش های عملکرد در برنامه های پایتون خود مجهز هستید.

این تکنیک های پروفایل را روی کد خود اعمال کنید و از آنچه کشف می کنید تعجب خواهید کرد. غالباً ، تنگناها جایی نیستند که انتظار دارید آنها باشند!

منابع و خواندن بیشتر

  • پیتون timeit مستندات: مستندات رسمی برای timeit ماژول ، با توضیحات مفصل در مورد همه پارامترها و توابع.

  • پیتون cProfile مستندات: راهنمای جامع برای پروفایل ماژول ها ، از جمله هر دو cProfile وت profileبشر

  • پیتون pstats مستندات: مرجع مفصل برای pstats ماژول ، توضیح کلیه روشهای تجزیه و تحلیل داده های پروفایل.

  • Python Profilers – رسمی Documentation: مستندات رسمی کامل روی قابلیت های پروفایل پایتون.

  • Python Speed ​​- نکات عملکرد: مجموعه ای از نکات عملی برای بهینه سازی کد پایتون پس از شناسایی تنگناها.

  • Python Profilers: توضیح عمیق در مورد پروفایل در پایتون ، از جمله جزئیات روی سربار و دقت.

  • تکنیک های بهینه سازی جنگو: مشاوره عملی روی بهینه سازی کد Django ORM در برنامه های دنیای واقعی.

  • روشهای جادویی پایتون چگونه کار می کنند: یک راهنمای عملی: مقاله قبلی من روی FreecodeCamp پوشش عمیق شیرجه روی روشهای جادویی پایتون.