Profiling & Performance
Mindset ของการ profiling
หัวข้อที่มีชื่อว่า “Mindset ของการ profiling”ก่อนจะ optimise อะไร คุณต้อง วัด ก่อน กฎเดียวกันใช้ได้ทั้งใน TypeScript และ Python: อย่าเดาว่า bottleneck อยู่ที่ไหน — profile ก่อน, optimise ทีหลัง, วัดอีกครั้ง
| เครื่องมือ Node.js | คู่เทียบ Python |
|---|---|
console.time / console.timeEnd | timeit.timeit() |
performance.now() | time.perf_counter() |
node --prof + node --prof-process | cProfile + pstats |
| Chrome DevTools flame graph | snakeviz (pip install snakeviz) |
clinic.js / 0x | py-spy (external, sampling profiler) |
timeit: microbenchmarks ที่แม่นยำ
หัวข้อที่มีชื่อว่า “timeit: microbenchmarks ที่แม่นยำ”timeit รัน snippet ซ้ำหลายพันรอบแล้วรายงานเวลารวม บทบาทเทียบได้กับ console.time แต่เข้มงวดกว่ามาก เพราะปิด garbage collection ระหว่างวัดเพื่อตัด noise ออก
// TypeScript — manual timingconsole.time("list comp");for (let i = 0; i < 10_000; i++) { Array.from({ length: 100 }, (_, i) => i ** 2);}console.timeEnd("list comp");import timeit
# timeit รัน snippet 10,000 ครั้ง; return เวลารวมในหน่วยวินาทีt = timeit.timeit( '[x**2 for x in range(100)]', number=10_000)print(f"List comprehension: {t:.4f}s")
# เปรียบเทียบสองแนวทางt2 = timeit.timeit( 'list(map(lambda x: x**2, range(100)))', number=10_000)print(f"map(): {t2:.4f}s")ลองเล่น — timeit ใน browser
หัวข้อที่มีชื่อว่า “ลองเล่น — timeit ใน browser”import timeit
# เปรียบเทียบ list comprehension vs map vs generator-to-listapproaches = { "list comp": '[x**2 for x in range(100)]', "map+list ": 'list(map(lambda x: x**2, range(100)))', "for loop ": 'r=[]; [r.append(x**2) for x in range(100)]',}
results = {}for name, stmt in approaches.items(): t = timeit.timeit(stmt, number=5000) results[name] = t print(f" {name}: {t:.4f}s")
fastest = min(results, key=results.get)print(f"\nFastest: {fastest}")Loading Python runtime (first run only)…
cProfile: function-level profiling
หัวข้อที่มีชื่อว่า “cProfile: function-level profiling”cProfile คือ deterministic profiler ที่ติดมากับ Python คอยบันทึกทุก function call ว่าแต่ละ function โดนเรียกกี่ครั้งและใช้เวลาไปเท่าไหร่ เทียบได้กับ node --prof ต่างกันตรงที่มีมาให้ในตัวและอ่านผลได้เลยโดยไม่ต้องหา tool เสริม
// Node.js — file-based profiling// node --prof server.js// node --prof-process isolate-*.log > profile.txtimport cProfileimport pstatsimport io
def slow_function(): total = 0 for i in range(1000): total += sum(range(i)) return total
# Profile specific functionpr = cProfile.Profile()pr.enable()slow_function()pr.disable()
# Print top 5 by cumulative times = io.StringIO()ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')ps.print_stats(5)print(s.getvalue())cProfile ใน browser
หัวข้อที่มีชื่อว่า “cProfile ใน browser”import cProfileimport pstatsimport io
def inner(n: int) -> int: return sum(range(n))
def middle(n: int) -> int: return sum(inner(i) for i in range(n))
def outer(n: int) -> int: return middle(n) + inner(n)
pr = cProfile.Profile()pr.enable()result = outer(200)pr.disable()
s = io.StringIO()ps = pstats.Stats(pr, stream=s).sort_stats("cumulative")ps.print_stats(8)print(f"Result: {result}")print()print(s.getvalue())Loading Python runtime (first run only)…
การอ่าน pstats output
หัวข้อที่มีชื่อว่า “การอ่าน pstats output”ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.004 0.004 script.py:4(slow_function) 1000 0.004 0.000 0.004 0.000 {built-in method builtins.sum}| Column | ความหมาย |
|---|---|
ncalls | จำนวนครั้งที่ฟังก์ชันถูกเรียก |
tottime | เวลาที่ใช้ใน function นี้ (ไม่รวม callees) |
cumtime | เวลา cumulative (รวมทุก sub-calls) |
percall | เวลาเฉลี่ยต่อ call |
Sort strategies:
sort_stats('cumulative')— หา call chains ที่ช้าที่สุด end-to-end (เริ่มที่นี่)sort_stats('tottime')— หา functions ที่ทำงานมากที่สุดด้วยตัวเองsort_stats('ncalls')— หา functions ที่ถูกเรียกโดยไม่คาดคิดบ่อยเกินไป
Workflow: profile → identify → fix → measure
หัวข้อที่มีชื่อว่า “Workflow: profile → identify → fix → measure”# 1. Profileimport cProfilecProfile.run('my_function()', 'profile.stats')
# 2. Inspect interactivelyimport pstatsp = pstats.Stats('profile.stats')p.sort_stats('cumulative').print_stats(10)
# 3. Visualise (ติดตั้งครั้งเดียว)# pip install snakeviz# snakeviz profile.stats # เปิด browser flame graph