ข้ามไปยังเนื้อหา

Profiling & Performance

ก่อนจะ optimise อะไร คุณต้อง วัด ก่อน กฎเดียวกันใช้ได้ทั้งใน TypeScript และ Python: อย่าเดาว่า bottleneck อยู่ที่ไหน — profile ก่อน, optimise ทีหลัง, วัดอีกครั้ง

เครื่องมือ Node.jsคู่เทียบ Python
console.time / console.timeEndtimeit.timeit()
performance.now()time.perf_counter()
node --prof + node --prof-processcProfile + pstats
Chrome DevTools flame graphsnakeviz (pip install snakeviz)
clinic.js / 0xpy-spy (external, sampling profiler)

timeit รัน snippet ซ้ำหลายพันรอบแล้วรายงานเวลารวม บทบาทเทียบได้กับ console.time แต่เข้มงวดกว่ามาก เพราะปิด garbage collection ระหว่างวัดเพื่อตัด noise ออก

TypeScript
// TypeScript — manual timing
console.time("list comp");
for (let i = 0; i < 10_000; i++) {
Array.from({ length: 100 }, (_, i) => i ** 2);
}
console.timeEnd("list comp");
Python
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")
import timeit
# เปรียบเทียบ list comprehension vs map vs generator-to-list
approaches = {
"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}")

cProfile คือ deterministic profiler ที่ติดมากับ Python คอยบันทึกทุก function call ว่าแต่ละ function โดนเรียกกี่ครั้งและใช้เวลาไปเท่าไหร่ เทียบได้กับ node --prof ต่างกันตรงที่มีมาให้ในตัวและอ่านผลได้เลยโดยไม่ต้องหา tool เสริม

TypeScript
// Node.js — file-based profiling
// node --prof server.js
// node --prof-process isolate-*.log > profile.txt
Python
import cProfile
import pstats
import io
def slow_function():
total = 0
for i in range(1000):
total += sum(range(i))
return total
# Profile specific function
pr = cProfile.Profile()
pr.enable()
slow_function()
pr.disable()
# Print top 5 by cumulative time
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
ps.print_stats(5)
print(s.getvalue())
import cProfile
import pstats
import 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())
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 ที่ถูกเรียกโดยไม่คาดคิดบ่อยเกินไป
# 1. Profile
import cProfile
cProfile.run('my_function()', 'profile.stats')
# 2. Inspect interactively
import pstats
p = pstats.Stats('profile.stats')
p.sort_stats('cumulative').print_stats(10)
# 3. Visualise (ติดตั้งครั้งเดียว)
# pip install snakeviz
# snakeviz profile.stats # เปิด browser flame graph
`timeit.timeit(stmt, number=10_000)` return อะไร?
ใน pstats output ของ cProfile `cumtime` หมายความว่าอะไร?
pstats sort key ใดที่หา functions ที่ consume เวลามากที่สุดด้วยตัวเอง (ไม่รวม sub-calls)?
ทำไม timeit จึงปิด garbage collection ระหว่างการรัน?