Lesson 35 — Benchmark & Optimization
Đo trước khi tối ưu, đo lại để xác nhận. Trang này có 3 công cụ tương tác giúp bạn cảm nhận lý thuyết.
Module 1 — Parse & so sánh output benchmark
Paste output go test -bench=. -benchmem vào 2 ô bên dưới. Hệ thống parse từng dòng, hiển thị table và tính tỷ lệ new vs old. Đây là phiên bản tay-mỏng của benchstat.
Đọc table: mỗi dòng là 1 benchmark. Cột
Δns/op là tỷ lệ thời gian mới/cũ — <1 là nhanh hơn. Tương tự ΔB/op cho memory. Khi cùng 1 tên benchmark xuất hiện ở cả 2 file → benchstat thật sẽ so sánh, ở đây ta dùng phép chia đơn giản.
Lưu ý: phép chia đơn giản này KHÔNG xét p-value. Để biết khác biệt có statistically significant, dùng
benchstat thật với -count=10 trở lên.
Module 2 — Common optimization picker
Click 1 pattern → xem code before/after + số đo thật từ solutions_test.go của lesson này.
—
❌ Trước
✅ Sau
Module 3 — Allocation tracker: append vs pre-alloc
Khi append vào slice cap=0, Go grow theo geometric (×2). Mỗi grow allocate slice mới + copy data cũ. Pre-alloc make([]int, 0, n) → 1 lần allocate, 0 lần copy. Kéo slider để thấy.
🔴 Naive — make([]int, 0)
Số lần allocate: —
Tổng bytes copy: —
Bytes wasted (orphan capacity): —
cap mới
bytes copy
🟢 PreAlloc — make([]int, 0, n)
Số lần allocate: 1
Tổng bytes copy: 0
Bytes wasted: 0
cap đúng
Quy luật grow trong Go runtime (đại khái, đã đơn giản hoá): cap mới = old × 2 nếu old < 256; sau đó tăng chậm hơn dần. Mỗi lần grow phải copy toàn bộ phần tử cũ → tổng bytes copy ≈
2 × n × 8 byte cho slice int.
Vì sao "wasted"? Cap cuối cùng thường lớn hơn n. Ví dụ n=1000 → cap có thể là 1280. 280 phần tử × 8 byte = 2240 byte không dùng nhưng vẫn chiếm RAM tới khi GC dọn.