Hướng Dẫn Tối Ưu Hiệu Năng Python: Dùng timeit và cProfile Để Tìm Code Chạy Chậm
- Home
- Hướng Dẫn Tối Ưu Hiệu Năng Python: Dùng timeit và cProfile Để Tìm Code Chạy Chậm
Trong quá trình phát triển phần mềm bằng Python, việc viết code chạy đúng chức năng mới chỉ là bước đầu. Khi hệ thống mở rộng hoặc xử lý lượng dữ liệu lớn, hiệu năng (performance) sẽ trở thành yếu tố quyết định.
Tuy nhiên, đừng đoán mò điểm nghẽn (bottleneck) của chương trình! Đo lường chính xác bằng các công cụ chuyên dụng như timeit và cProfile là cách duy nhất giúp bạn biết chính xác đoạn code nào đang làm chậm hệ thống để tối ưu đúng chỗ.
timeitModule timeit được tích hợp sẵn trong thư viện chuẩn của Python. Nó được thiết kế riêng để đo thời gian thực thi của các đoạn code nhỏ hoặc hàm đơn lẻ bằng cách chạy lặp lại nhiều lần để tránh nhiễu từ hệ điều hành.
timeit?So sánh tốc độ giữa 2 hoặc nhiều cách viết code cùng một logic (ví dụ: dùng for loop vs list comprehension).
Kiểm tra hiệu năng của một thuật toán/hàm nhỏ.
timeit trong Python ScriptPython
import timeit
# Code ví dụ 1: Dùng vòng lặp for
code_for = """
result = []
for i in range(1000):
result.append(i * 2)
"""
# Code ví dụ 2: Dùng List Comprehension
code_comprehension = """
result = [i * 2 for i in range(1000)]
"""
# Chạy mỗi đoạn code 10,000 lần
time_for = timeit.timeit(stmt=code_for, number=10000)
time_comp = timeit.timeit(stmt=code_comprehension, number=10000)
print(f"Vòng lặp for: {time_for:.4f} giây")
print(f"List Comprehension: {time_comp:.4f} giây")
timeit qua Command Line (CLI)Bạn có thể đo nhanh một câu lệnh ngay từ Terminal mà không cần tạo file .py:
Bash
python -m timeit "[i * 2 for i in range(1000)]"
cProfileKhi bạn có một dự án lớn hoặc file script gồm nhiều hàm gọi lẫn nhau, timeit không còn phù hợp. Lúc này, bạn cần cProfile – công cụ profiling (phân tích hiệu năng) c-extension tích hợp sẵn trong Python.
cProfile sẽ theo dõi toàn bộ luồng thực thi, đếm số lần mỗi hàm được gọi và tổng thời gian tiêu tốn ở từng hàm.
cProfile?Tìm hàm/module nào đang chiếm nhiều thời gian nhất trong toàn bộ ứng dụng.
Phân tích tổng thể một luồng xử lý phức tạp.
cProfile từ Command LineCách đơn giản nhất là chạy script của bạn thông qua cProfile:
Bash
python -m cProfile -s cumtime main.py
Tham số -s cumtime giúp sắp xếp kết quả theo thời gian tích lũy (cumulative time) từ cao xuống thấp.
Khi kết quả hiển thị, bạn sẽ thấy các cột thông số quan trọng sau:
| Cột | Ý nghĩa |
| ncalls | Số lần hàm được gọi. |
| tottime | Tổng thời gian thực thi bên trong hàm đó (không tính thời gian của các hàm con được nó gọi). |
| percall | tottime chia cho ncalls. |
| cumtime | Thời gian tích lũy thực thi hàm đó (bao gồm cả thời gian chạy các hàm con). |
| filename:lineno(function) | Vị trí file, dòng code và tên hàm tương ứng. |
cProfile trực tiếp vào code PythonNếu chỉ muốn profile một hàm cụ thể trong ứng dụng:
Python
import cProfile
import pstats
def process_data():
# Giả lập logic xử lý dữ liệu
data = [x**2 for x in range(1000000)]
return sum(data)
# Khởi tạo Profiler
profiler = cProfile.Profile()
profiler.enable()
# Gọi hàm cần phân tích
process_data()
profiler.disable()
# In kết quả đã sắp xếp theo thời gian tích lũy
stats = pstats.Stats(profiler).sort_stats('cumtime')
stats.print_stats(10) # In top 10 hàm tốn thời gian nhất
Để tối ưu ứng dụng hiệu quả mà không tốn thời gian vô ích, hãy áp dụng quy trình sau:
Profile bằng cProfile: Tìm ra chính xác “vùng nguy hiểm” (hotspot) – hàm nào đang tiêu tốn nhiều tài nguyên nhất.
Thử nghiệm giải pháp với timeit: Viết các phương án tối ưu khác nhau cho hàm đó và dùng timeit để chọn ra phương án nhanh nhất.
Refactor & Kiểm tra lại: Đổi code cũ sang code mới, sau đó chạy lại cProfile trên toàn bộ hệ thống để xác nhận hiệu năng đã được cải thiện.