Pandas & NumPy Academy · Pelajaran

Pemprofilan dengan timeit dan memory_profiler

Ukur masa pelaksanaan dengan %timeit dan memori puncak dengan memory_profiler untuk mengenal pasti bahagian pipeline yang paling perlahan.

Pelajaran 1 daripada 413 langkah

Pemprofilan dengan timeit dan memory_profiler ialah pelajaran Pandas & NumPy Academy percuma di CoddyKit. Ini ialah pelajaran 1 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Pandas & NumPy Academy, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Pandas & NumPy Academy merangkumi sejumlah 4 pelajaran.

Mengapakah Membuat Profil Sebelum Mengoptimumkan?

Kesilapan yang lazim ialah mengoptimumkan kod berdasarkan intuisi — menulis semula fungsi yang kelihatan perlahan tanpa mengukur sama ada fungsi itu benar-benar menjadi titik sempit. Dalam saluran pemprosesan data, 90% masa pelaksanaan selalunya datang daripada 10% kod. Pemprofilan mengenal pasti dengan tepat tempat masa dan memori digunakan supaya Anda boleh memfokuskan usaha pengoptimuman pada bahagian yang penting. Peraturan emas: ukur dahulu, optimumkan kemudian. Alat seperti timeit mengukur masa dan memory_profiler mengukur RAM.

import pandas as pd
import numpy as np
import timeit

# Naive approach vs vectorised approach — which is faster?
np.random.seed(0)
df = pd.DataFrame({'a': np.random.randn(100000), 'b': np.random.randn(100000)})

t1 = timeit.timeit(lambda: df['a'] + df['b'], number=100)
t2 = timeit.timeit(lambda: [a + b for a, b in zip(df['a'], df['b'])], number=10)

print(f'Vectorised (100 runs): {t1:.3f}s')
print(f'List comprehension (10 runs): {t2:.3f}s')
print(f'Per-run speedup: ~{(t2/10)/(t1/100):.0f}x')

timeit: Pemasa Terbina Dalam Python

Modul timeit mengukur masa pelaksanaan dengan menjalankan pernyataan berkali-kali dan mengembalikan jumlah masa yang berlalu. timeit.timeit(stmt, number=n) menjalankan stmt sebanyak n kali dan mengembalikan jumlah saat. Bahagikan dengan number untuk mendapatkan masa bagi setiap pelaksanaan. Untuk pengukuran yang tepat, pilih number supaya jumlah pelaksanaan mengambil sekurang-kurangnya 0.1 saat — satu pelaksanaan operasi pantas tidak mewakili keadaan sebenar kerana hingar penjadualan sistem.

import timeit
import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({'x': np.random.randn(10000)})

# Compare three ways to square a column
time_a = timeit.timeit(lambda: df['x'] ** 2, number=1000)
time_b = timeit.timeit(lambda: df['x'].apply(lambda v: v**2), number=100)
time_c = timeit.timeit(lambda: np.square(df['x']), number=1000)

print(f'Operator **2  per run: {time_a/1000*1000:.3f} ms')
print(f'.apply(v**2) per run: {time_b/100*1000:.3f} ms')
print(f'np.square()  per run: {time_c/1000*1000:.3f} ms')

Keajaiban %timeit dalam Jupyter

Dalam buku nota Jupyter, keajaiban %timeit memilih bilangan ulangan dan pelaksanaan secara automatik untuk memberikan anggaran yang stabil dari segi statistik, kemudian melaporkan masa terbaik dan purata bersama sisihan piawai. Ini ialah cara paling mudah untuk menanda aras satu baris dengan cepat. Gunakan %%timeit (peratus berganda) untuk mengukur seluruh sel. Format output ialah: X ns/μs/ms ± Y ns per gelung (purata ± sisihan piawai bagi 7 pelaksanaan, N gelung setiap satu).

# In Jupyter notebook:
# %timeit df['x'] ** 2
# Output: 47.3 us +- 1.2 us per loop (mean +- std. dev. of 7 runs, 10000 loops each)

# Multi-line cell timing:
# %%timeit
# result = df['x'] ** 2
# total = result.sum()

# In a regular Python script, use timeit.timeit() instead:
import timeit
import pandas as pd, numpy as np
df = pd.DataFrame({'x': np.random.randn(10000)})
result = timeit.repeat(lambda: df['x']**2, number=1000, repeat=7)
print(f'Best run: {min(result)/1000*1000:.3f} ms per call')

cProfile untuk Pemprofilan Peringkat Fungsi

Walaupun timeit mengukur satu ungkapan, cProfile membuat profil bagi seluruh tindanan panggilan — ia menunjukkan jumlah masa yang dihabiskan dalam setiap fungsi yang dipanggil semasa pelaksanaan. Ini penting untuk mengenal pasti titik sempit dalam saluran pemprosesan yang kompleks. Jalankan python -m cProfile -s cumulative script.py daripada baris arahan, atau gunakan cProfile.run('function()') dalam skrip. Dalam Jupyter, gunakan %prun function().

import cProfile
import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({'a': np.random.randn(50000), 'b': np.random.randn(50000)})

def slow_pipeline(df):
    result = []
    for _, row in df.iterrows():
        result.append(row['a'] * row['b'])
    return pd.Series(result)

# Profile the function
print('Profile output (top 5 functions by cumulative time):')
cProfile.run('slow_pipeline(df.head(1000))', sort='cumulative')

Mengukur Penggunaan Memori

Gunakan df.memory_usage(deep=True) untuk melihat jumlah RAM yang digunakan oleh setiap lajur. Argumen deep=True mengukur memori sebenar yang digunakan oleh lajur jenis objek (rentetan), yang sebaliknya hanya melaporkan saiz penuding. Gunakan .sum() untuk mendapatkan jumlah memori DataFrame dalam bait. Bahagikan dengan 1e6 untuk MB atau 1e9 untuk GB. Ini ialah titik permulaan bagi pengoptimuman memori — Anda tidak boleh mengurangkan perkara yang belum diukur.

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'id': range(100000),
    'name': ['user_' + str(i) for i in range(100000)],
    'score': np.random.randn(100000),
    'category': np.random.choice(['A', 'B', 'C', 'D'], 100000)
})

mem = df.memory_usage(deep=True)
print('Memory per column (bytes):')
print(mem)
print(f'\nTotal: {mem.sum() / 1e6:.2f} MB')

memory_profiler untuk Memori Baris demi Baris

Pakej memory_profiler (pip install memory-profiler) mengukur penggunaan RAM baris demi baris dalam fungsi. Hiasi fungsi dengan @profile dan jalankan dengan python -m memory_profiler script.py. Dalam Jupyter, gunakan keajaiban %memit (daripada sambungan ipython) untuk mengukur memori puncak bagi satu ungkapan. Ini mengenal pasti struktur data yang menyebabkan lonjakan memori secara tiba-tiba — selalunya kerana menggabungkan dalam gelung dan bukannya mengumpulkan data kemudian menggabungkannya sekali.

# Install: pip install memory-profiler
# Usage as a decorator:

# from memory_profiler import profile
# @profile
# def process_data():
#     import pandas as pd, numpy as np
#     df = pd.DataFrame({'x': np.random.randn(1000000)})
#     result = df['x'].rolling(100).mean()    # <-- sees if this spikes RAM
#     return result

# Run: python -m memory_profiler script.py

# In Jupyter (after %load_ext memory_profiler):
# %memit pd.DataFrame({'x': np.random.randn(1000000)}).rolling(100).mean()

print('memory_profiler decorates functions for line-by-line RAM measurement.')
print('Typical output: Line # Mem usage Increment Line Contents')

sys.getsizeof berbanding memory_usage untuk Objek

sys.getsizeof(obj) mengembalikan saiz memori bagi objek Python, tetapi bagi DataFrame pandas, ia hanya melaporkan overhed bekas (beberapa ratus bait), bukannya data sebenar. Sentiasa gunakan df.memory_usage(deep=True).sum() untuk DataFrame. Bagi objek Python individu seperti senarai dan kamus, sys.getsizeof adalah tepat tetapi tidak menelusuri objek bersarang — gunakan pustaka pympler (asizeof) untuk mendapatkan saiz objek secara mendalam.

import sys
import pandas as pd
import numpy as np

df = pd.DataFrame({'x': np.random.randn(100000)})

# sys.getsizeof reports only the container metadata — misleading!
print('sys.getsizeof(df):', sys.getsizeof(df), 'bytes (misleading!)')

# memory_usage gives the true memory
true_mem = df.memory_usage(deep=True).sum()
print('df.memory_usage(deep=True).sum():', true_mem, 'bytes')
print(f'True memory: {true_mem/1e6:.2f} MB')

Corak Pengurus Konteks untuk Mengukur Masa

Bagi langkah saluran pemprosesan yang lebih panjang apabila timeit tidak sesuai (contohnya, pertanyaan pangkalan data atau I/O fail), gunakan pengurus konteks mudah dengan time.perf_counter(). Balut setiap langkah saluran pemprosesan dalam konteks pemasa untuk merekod tempohnya. Ini lebih praktikal daripada timeit untuk kod pengeluaran kerana ia merekod masa jam sebenar termasuk penantian I/O, yang turut diukur oleh timeit tetapi lebih sukar diintegrasikan dengan rangka kerja pengelogan.

import time
import contextlib
import pandas as pd
import numpy as np

@contextlib.contextmanager
def timer(name):
    start = time.perf_counter()
    yield
    elapsed = time.perf_counter() - start
    print(f'[{name}] {elapsed*1000:.1f} ms')

np.random.seed(0)
df = pd.DataFrame({'x': np.random.randn(500000), 'y': np.random.randn(500000)})

with timer('rolling mean'):
    result1 = df['x'].rolling(30).mean()

with timer('groupby mean'):
    df['group'] = df['x'].round(0)
    result2 = df.groupby('group')['y'].mean()

Pemprofilan Operasi Pandas Secara Sistematik

Aliran kerja pemprofilan yang sistematik: 1) Mulakan dengan sampel data yang mewakili data anda (10,000 baris sudah mencukupi untuk perbandingan masa). 2) Kenal pasti langkah paling perlahan menggunakan konteks pemasa atau cProfile. 3) Tulis semula langkah paling perlahan menggunakan pendekatan yang lebih pantas (vektorisasi, dtype Kategori, sambungan C). 4) Sahkan ketepatan menggunakan pernyataan assert. 5) Ukur semula masa untuk mengesahkan peningkatan kelajuan. 6) Ulangi proses untuk kesesakan seterusnya sehingga pipeline memenuhi SLA.

import pandas as pd
import numpy as np
import timeit

np.random.seed(0)
df = pd.DataFrame({
    'value': np.random.randn(100000),
    'category': np.random.choice(['A', 'B', 'C', 'D', 'E'], 100000)
})

# Benchmark two groupby implementations
t1 = timeit.timeit(lambda: df.groupby('category')['value'].mean(), number=100)
df2 = df.copy()
df2['category'] = df2['category'].astype('category')
t2 = timeit.timeit(lambda: df2.groupby('category')['value'].mean(), number=100)

print(f'Object dtype groupby: {t1/100*1000:.2f} ms per call')
print(f'Categorical dtype:    {t2/100*1000:.2f} ms per call')
print(f'Speedup: {t1/t2:.1f}x')

Bila Perlu Membuat Pemprofilan: Ambang Praktikal

Bukan setiap skrip memerlukan pemprofilan. Laburkan usaha dalam pemprofilan apabila: pipeline mengambil masa lebih daripada 30 saat untuk dijalankan dan kerap dijalankan (setiap hari atau lebih kerap), fungsi memproses lebih daripada 100,000 baris, atau penggunaan memori melebihi 50% daripada RAM yang tersedia. Bagi skrip yang dijalankan sekali atau siap dalam beberapa saat, kebolehbacaan lebih penting daripada pengoptimuman mikrosaat. Kos menyelenggara kod yang sangat dioptimumkan tetapi sukar difahami sering melebihi masa yang dijimatkan oleh pengoptimuman tersebut.

Ringkasan: Gambaran Keseluruhan Alat Pemprofilan

Rujukan pantas:

  • timeit.timeit — mengukur masa satu pernyataan (skrip)
  • %timeit / %%timeit — mengukur masa dalam Jupyter (mengulangi secara automatik)
  • %prun / cProfile — pemprofilan tindanan panggilan pada peringkat fungsi
  • df.memory_usage(deep=True) — pengukuran RAM bagi setiap lajur
  • memory_profiler @profile — pengukuran RAM baris demi baris
  • time.perf_counter() — pemasa manual untuk langkah pipeline
Sentiasa lakukan pemprofilan menggunakan sampel data yang mewakili dan sahkan pengoptimuman dengan semakan assert sebelum menggunakannya dalam pengeluaran.

Semakan Pantas

Uji pemahaman anda tentang alat pemprofilan daripada pelajaran ini.

Imbas Kembali Pelajaran

Dalam pelajaran ini, anda mempelajari bahawa timeit.timeit dan %timeit mengukur masa pelaksanaan operasi tertentu, df.memory_usage(deep=True) mengukur RAM bagi setiap lajur dengan tepat, dan cProfile / memory_profiler menyediakan pemprofilan yang lebih mendalam untuk pipeline yang kompleks. Sentiasa lakukan pemprofilan sebelum mengoptimumkan, dan sahkan ketepatan selepas setiap perubahan. Seterusnya, kita akan meneroka cara mengelakkan gelung iterrows yang perlahan dan menggantikannya dengan operasi tervекторisasi.

Percuma untuk bermula

Pelajari Python dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
30
Pelajaran
120

Soalan Lazim

Adakah pelajaran “Pemprofilan dengan timeit dan memory_profiler” percuma?

Ya — teks penuh “Pemprofilan dengan timeit dan memory_profiler” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Pandas & NumPy Academy, tingkat taraf kepada CoddyKit PRO. Kursus Pandas & NumPy Academy merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Pemprofilan dengan timeit dan memory_profiler”?

Ukur masa pelaksanaan dengan %timeit dan memori puncak dengan memory_profiler untuk mengenal pasti bahagian pipeline yang paling perlahan. Anda berlatih Pandas & NumPy Academy menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Pandas & NumPy Academy?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Pandas & NumPy Academy di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 1 daripada 4.

Berapa lamakah pelajaran “Pemprofilan dengan timeit dan memory_profiler” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Pandas & NumPy Academy ini?

Ya. Setiap pelajaran Pandas & NumPy Academy menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Pemprofilan dengan timeit dan memory_profiler
  2. Mengelakkan iterrows dan Gelung Python
  3. Jenis Data Cekap untuk Mengurangkan Memori
  4. Pembacaan Berkelompok untuk Fail Besar
← Kembali ke Pandas & NumPy Academy