Pandas & NumPy Academy · Pelajaran

Pemrofilan dengan timeit dan memory_profiler

Ukur waktu eksekusi dengan %timeit dan memori puncak dengan memory_profiler untuk menemukan bagian pipeline yang paling lambat.

Pelajaran 1 dari 413 langkah

Pemrofilan dengan timeit dan memory_profiler adalah pelajaran Pandas & NumPy Academy gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Pandas & NumPy Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Pandas & NumPy Academy mencakup 4 pelajaran total.

Mengapa Membuat Profil Sebelum Mengoptimalkan?

Kesalahan yang umum terjadi adalah mengoptimalkan kode berdasarkan intuisi — menulis ulang fungsi yang terlihat lambat tanpa mengukur apakah fungsi tersebut benar-benar menjadi hambatan. Dalam alur data, 90% waktu eksekusi sering kali berasal dari 10% kode. Pembuatan profil mengidentifikasi secara tepat bagian yang menggunakan waktu dan memori, sehingga Anda dapat memusatkan upaya optimasi pada bagian yang paling berpengaruh. Aturan utamanya: ukur terlebih dahulu, optimalkan kemudian. Alat seperti timeit mengukur waktu, sedangkan 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: Pengukur Waktu Bawaan Python

Modul timeit mengukur waktu eksekusi dengan menjalankan pernyataan berkali-kali dan mengembalikan total waktu yang berlalu. timeit.timeit(stmt, number=n) menjalankan stmt sebanyak n kali dan mengembalikan total dalam detik. Bagilah dengan number untuk mendapatkan waktu per eksekusi. Untuk pengukuran yang akurat, pilih number agar total waktu berjalan setidaknya 0,1 detik — satu kali eksekusi operasi cepat tidak representatif karena gangguan penjadwalan 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')

Sihir %timeit di Jupyter

Dalam notebook Jupyter, sihir %timeit secara otomatis memilih jumlah pengulangan dan eksekusi untuk memberikan perkiraan yang stabil secara statistik, lalu melaporkan waktu terbaik dan rata-rata beserta simpangan bakunya. Ini merupakan cara paling praktis untuk melakukan tolok ukur dengan cepat pada satu baris. Gunakan %%timeit (persen ganda) untuk mengukur waktu seluruh sel. Format hasilnya adalah: X ns/μs/ms ± Y ns per loop (mean ± std. dev. of 7 runs, N loops each).

# 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 Pembuatan Profil Tingkat Fungsi

Sementara timeit mengukur satu ekspresi, cProfile membuat profil seluruh tumpukan pemanggilan — alat ini menunjukkan waktu yang digunakan oleh setiap fungsi yang dipanggil selama eksekusi. Hal ini penting untuk mengidentifikasi hambatan dalam alur data yang kompleks. Jalankan python -m cProfile -s cumulative script.py dari baris perintah, atau gunakan cProfile.run('function()') dalam sebuah skrip. Di 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 setiap kolom. Argumen deep=True mengukur memori sebenarnya yang digunakan kolom bertipe objek (string), yang jika tidak digunakan hanya melaporkan ukuran penunjuk. Gunakan .sum() untuk mendapatkan total memori DataFrame dalam byte. Bagilah dengan 1e6 untuk mendapatkan MB atau 1e9 untuk mendapatkan GB. Ini merupakan titik awal optimasi memori — Anda tidak dapat mengurangi sesuatu yang belum Anda ukur.

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

Paket memory_profiler (pip install memory-profiler) mengukur penggunaan RAM baris demi baris dalam sebuah fungsi. Hiasi fungsi dengan @profile dan jalankan dengan python -m memory_profiler script.py. Di Jupyter, gunakan sihir %memit (dari ekstensi ipython) untuk mengukur memori puncak dari satu ekspresi. Ini mengidentifikasi struktur data yang menyebabkan lonjakan memori mendadak — sering kali karena melakukan penggabungan dalam perulangan, bukan mengumpulkan data lalu 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 vs memory_usage untuk Objek

sys.getsizeof(obj) mengembalikan ukuran memori sebuah objek Python, tetapi untuk DataFrame pandas, fungsi ini hanya melaporkan overhead wadahnya (beberapa ratus byte), bukan data sebenarnya. Selalu gunakan df.memory_usage(deep=True).sum() untuk DataFrame. Untuk objek Python individual seperti daftar dan kamus, sys.getsizeof akurat, tetapi tidak menelusuri objek bersarang — gunakan pustaka pympler (asizeof) untuk mendapatkan ukuran objek secara menyeluruh.

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')

Pola Pengelola Konteks untuk Pengukuran Waktu

Untuk langkah alur data yang lebih panjang, ketika timeit tidak sesuai (misalnya kueri basis data atau I/O file), gunakan pengelola konteks sederhana dengan time.perf_counter(). Bungkus setiap langkah alur data dalam konteks pengukur waktu untuk mencatat durasinya. Cara ini lebih praktis daripada timeit untuk kode produksi karena mencatat waktu nyata yang berlalu, termasuk waktu tunggu I/O, yang juga diukur oleh timeit tetapi lebih sulit diintegrasikan dengan kerangka kerja pencatatan.

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()

Membuat Profil Operasi Pandas Secara Sistematis

Alur kerja pembuatan profil yang sistematis: 1) Mulailah dengan sampel data yang representatif (10.000 baris sudah cukup untuk perbandingan waktu). 2) Identifikasi langkah yang paling lambat menggunakan konteks pengukur waktu atau cProfile. 3) Tulis ulang langkah yang paling lambat menggunakan pendekatan yang lebih cepat (vektorisasi, dtype kategorikal, ekstensi C). 4) Verifikasi kebenaran dengan pernyataan assert. 5) Ukur kembali waktunya untuk memastikan peningkatan kecepatan. 6) Ulangi proses untuk hambatan berikutnya hingga pipeline memenuhi SLA-nya.

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')

Kapan Membuat Profil: Ambang Batas Praktis

Tidak semua skrip memerlukan pembuatan profil. Lakukan pembuatan profil ketika: pipeline memerlukan waktu lebih dari 30 detik untuk berjalan dan sering dijalankan (setiap hari atau lebih sering), sebuah fungsi memproses lebih dari 100.000 baris, atau penggunaan memori melebihi 50% RAM yang tersedia. Untuk skrip yang hanya dijalankan sekali atau selesai dalam hitungan detik, keterbacaan lebih penting daripada optimisasi tingkat mikrodetik. Biaya pemeliharaan kode yang sangat dioptimalkan tetapi sulit dipahami sering kali lebih besar daripada waktu yang dihemat oleh optimisasi tersebut.

Ringkasan Singkat Alat Pembuatan Profil

Referensi cepat:

  • timeit.timeit — mengukur waktu satu pernyataan (skrip)
  • %timeit / %%timeit — mengukur waktu di Jupyter (mengulangi secara otomatis)
  • %prun / cProfile — pembuatan profil tumpukan pemanggilan pada tingkat fungsi
  • df.memory_usage(deep=True) — mengukur RAM per kolom
  • memory_profiler @profile — mengukur RAM baris demi baris
  • time.perf_counter() — pengukur waktu manual untuk langkah-langkah pipeline
Selalu buat profil menggunakan sampel data yang representatif dan pastikan optimisasi dengan pemeriksaan assert sebelum memindahkannya ke lingkungan produksi.

Pemeriksaan Cepat

Uji pemahaman Anda tentang alat pembuatan profil dari pelajaran ini.

Ringkasan Pelajaran

Dalam pelajaran ini Anda mempelajari bahwa timeit.timeit dan %timeit mengukur waktu eksekusi operasi tertentu, df.memory_usage(deep=True) mengukur RAM per kolom secara akurat, dan cProfile / memory_profiler menyediakan pembuatan profil yang lebih mendalam untuk pipeline yang kompleks. Selalu buat profil sebelum melakukan optimisasi, dan verifikasi kebenaran setelah setiap perubahan. Berikutnya kita akan membahas cara menghindari loop iterrows yang lambat dan menggantinya dengan operasi bervectorisasi.

Gratis untuk memulai

Belajar Python dengan tutor AI — gratis

Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.

Kursus
30
Pelajaran
120

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Pemrofilan dengan timeit dan memory_profiler” gratis?

Ya — teks lengkap “Pemrofilan dengan timeit dan memory_profiler” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Pandas & NumPy Academy, upgrade ke CoddyKit PRO. Kursus Pandas & NumPy Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Pemrofilan dengan timeit dan memory_profiler”?

Ukur waktu eksekusi dengan %timeit dan memori puncak dengan memory_profiler untuk menemukan bagian pipeline yang paling lambat. Kamu berlatih Pandas & NumPy Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Pandas & NumPy Academy?

Tidak diperlukan pengalaman sebelumnya. Pandas & NumPy Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.

Berapa lama pelajaran “Pemrofilan dengan timeit dan memory_profiler” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Pandas & NumPy Academy ini?

Ya. Setiap pelajaran Pandas & NumPy Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Pemrofilan dengan timeit dan memory_profiler
  2. Menghindari iterrows dan Perulangan Python
  3. Tipe Data Efisien untuk Mengurangi Memori
  4. Membaca File Besar dalam Potongan
← Kembali ke Pandas & NumPy Academy