Notes & software courses · Free to learn
Aph's Blog
On this page

Profiling — วัดว่าช้าตรงไหน

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

หาจุดที่ทำให้โปรแกรมช้าด้วยข้อมูลจริงจาก timeit และ cProfile แทนการเดา

เมื่อโปรแกรมช้า สัญชาตญาณมักเดาผิดว่าช้าตรงไหน การ profiling คือ "วัดของจริง" ว่าเวลาหมดไปกับส่วนไหน เพื่อจะได้แก้ถูกจุด — กฎข้อแรกของการ optimize คือ วัดก่อน อย่าเดา

timeit — วัดโค้ดเล็ก ๆ

ใช้เทียบว่า 2 วิธีเขียน วิธีไหนเร็วกว่า timeit รันโค้ดซ้ำหลายครั้งแล้วเฉลี่ย ให้ผลแม่นกว่าจับเวลาเอง

python
import timeit

# เทียบ: สร้าง list ด้วย loop vs comprehension
loop_time = timeit.timeit(
    "result = []\nfor i in range(1000): result.append(i*i)",
    number=10000,
)
comp_time = timeit.timeit(
    "result = [i*i for i in range(1000)]",
    number=10000,
)
print(f"loop: {loop_time:.3f}s")
print(f"comprehension: {comp_time:.3f}s")   # มักเร็วกว่า

time.perf_counter — จับเวลาช่วงโค้ดจริง

python
import time

start = time.perf_counter()
total = sum(i * i for i in range(10_000_000))
elapsed = time.perf_counter() - start
print(f"ใช้เวลา {elapsed:.3f} วินาที")
perf_counter ไม่ใช่ time.time

ใช้ time.perf_counter() สำหรับวัดช่วงเวลา (ความละเอียดสูง) ไม่ใช่ time.time() ที่ไว้ดูเวลานาฬิกาจริง — จำสำนวนนี้ได้จาก decorator @timer ในบท 1

cProfile — profile ทั้งโปรแกรม

เมื่อโปรแกรมใหญ่ ไม่รู้ว่าช้าตรงฟังก์ชันไหน cProfile วัดทุกฟังก์ชันให้ว่าถูกเรียกกี่ครั้งและใช้เวลาเท่าไร

python
import cProfile

def slow_function():
    return sum(i ** 2 for i in range(1_000_000))

def main():
    for _ in range(10):
        slow_function()

cProfile.run("main()")
bash
# หรือรันทั้งไฟล์จาก command line
python -m cProfile -s cumtime myscript.py

ผลลัพธ์มีคอลัมน์สำคัญ: ncalls (ถูกเรียกกี่ครั้ง), tottime (เวลาในฟังก์ชันเองไม่รวมลูก), cumtime (เวลารวมทั้งที่เรียกฟังก์ชันอื่น) — ดู cumtime สูง ๆ คือจุดที่ควรแก้ก่อน

อย่า optimize ก่อนวัด

"Premature optimization is the root of all evil" — อย่าเสียเวลาแต่งโค้ดให้เร็วในจุดที่ไม่ได้ช้าจริง เขียนให้ถูกและอ่านง่ายก่อน พอช้าค่อย profile หาจุดจริงแล้วแก้เฉพาะจุดนั้น (เชื่อมกับ Big-O ในบท DSA)

สรุปหัวข้อนี้

  • timeit เทียบความเร็ว 2 วิธีเขียน (รันซ้ำเฉลี่ย)
  • time.perf_counter() จับเวลาช่วงโค้ดจริง (ไม่ใช่ time.time)
  • cProfile วัดทุกฟังก์ชัน — ดู cumtime สูงคือจุดแก้ก่อน
  • วัดก่อนแก้เสมอ — อย่า optimize จุดที่ไม่ได้ช้าจริง
แบบฝึกหัด

1) ใช้ timeit เทียบ "x in list" กับ "x in set" บนข้อมูล 10000 ตัว 2) ใช้ perf_counter จับเวลาฟังก์ชันที่เขียนเอง 3) ใช้ cProfile กับโปรแกรมที่มีหลายฟังก์ชัน หาว่า cumtime สูงสุดอยู่ที่ไหน 4) อธิบายความหมายของ premature optimization