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.