Mấy bài qua ta gặp //go:noinline rải rác. Nó thuộc một họ lớn hơn: compiler directive — những chú thích đặc biệt bắt đầu bằng //go: ra lệnh trực tiếp cho compiler hoặc linker. Chúng trải từ tiện lợi vô hại (//go:embed) đến mạnh mẽ nguy hiểm (//go:linkname, //go:nosplit). Bài này tổng hợp các directive quan trọng, đo thật cái nào làm gì, và chỉ ra một cạm bẫy cú pháp khiến chúng vô hiệu âm thầm.
Nhóm an toàn, hay dùng
Những directive này dùng hằng ngày, không nguy hiểm:
//go:embed data.txt
var duLieu string // nhúng nội dung file vào biến lúc BUILD (cần import _ "embed")
//go:generate stringer -type=Mau // chạy khi 'go generate', KHÔNG lúc build
//go:noinline // chặn inline (cho benchmark/pprof — bài trước)
func f() int { ... }
//go:embed nhúng nội dung file thẳng vào binary lúc build — không đọc file lúc chạy. //go:generate khai một lệnh chạy khi bạn gõ go generate (không tự chạy lúc build). Cả hai an toàn và tiện.

Hình 1: Bảng directive //go:. Nhóm an toàn (embed, generate, noinline), nhóm tầng thấp nguy hiểm (linkname, nosplit, noescape), và cạm bẫy cú pháp khoảng trắng.
Nhóm tầng thấp, nguy hiểm
Những directive này mạnh nhưng phá vỡ các bảo đảm của Go:
//go:linkname nanotime runtime.nanotime // nối tới hàm RIÊNG của runtime (cần import _ "unsafe")
func nanotime() int64
//go:nosplit // bỏ prologue kiểm stack → có thể TRÀN STACK
//go:noescape // khai hàm asm không để tham số escape → sai là HỎNG BỘ NHỚ
//go:linkname cho phép gọi một symbol không export ở package khác — kể cả hàm nội bộ của runtime. //go:nosplit bỏ đoạn kiểm tra stack (như đã thấy ở bài đọc assembly), có thể tràn stack nếu hàm dùng nhiều. //go:noescape khai rằng một hàm assembly không để tham số escape — sai là hỏng bộ nhớ âm thầm.
Đo thật: chúng hoạt động
//go:embed nhúng file data.txt (chứa config-noi-dung):
embed: "config-noi-dung\n" // nội dung file đã vào biến, không đọc file lúc chạy
//go:linkname nối một hàm cục bộ tới runtime.nanotime (đồng hồ nội bộ của runtime, không export), rồi dùng nó đo một vòng lặp 1 triệu:
//go:linkname nanotime runtime.nanotime
linkname runtime.nanotime: 257000 ns // đo 1 triệu vòng cộng

Hình 2: //go:embed nhúng file thật (in ra config-noi-dung), //go:linkname gọi runtime.nanotime (đo 257000 ns), và cạm bẫy khoảng trắng khiến directive vô hiệu.
Cạm bẫy: một khoảng trắng làm directive vô hiệu
Đây là bẫy nguy hiểm nhất vì nó im lặng. Directive phải viết //go:name — không có khoảng trắng sau //, và sát ngay dòng trên khai báo (không dòng trống chen giữa). Sai một trong hai, directive biến thành comment thường:
// go:noinline → SAI: có space sau // → chỉ là comment, BỎ QUA
//go:noinline → ĐÚNG: không space, sát khai báo
Đo thật: hàm với // go:noinline (có space) vẫn được inline — go build -gcflags=-m in can inline saiCuPhap. Không lỗi, không cảnh báo, directive chỉ âm thầm vô hiệu. Đây là lý do luôn phải kiểm directive có tác dụng thật (bằng -gcflags=-m, đo hành vi, hoặc kiểm output).
Ứng dụng thực tế
//go:embed thay việc đọc file cấu hình lúc chạy. Nhúng template, file tĩnh web, cấu hình mặc định thẳng vào binary — triển khai một file duy nhất, không lo đường dẫn file lúc chạy. Đây là một trong những tính năng tiện nhất Go thêm gần đây (1.16).
//go:generate để sinh code lặp lại. Khai lệnh sinh code (stringer cho enum, mock cho interface, code từ protobuf) ngay cạnh khai báo. Chạy go generate ./... sinh lại tất cả. Giữ code sinh ra đồng bộ với nguồn.
//go:linkname là lối thoát hiểm, không phải công cụ thường. Đôi khi cần một hàm runtime không export (đo thời gian tầng thấp, truy cập nội bộ scheduler). linkname cho phép, nhưng bạn phụ thuộc vào chi tiết nội bộ có thể đổi bất kỳ phiên bản nào. Một số thư viện nổi tiếng dùng nó, nhưng đó là nợ kỹ thuật.
Đánh đổi cần cân nhắc
Directive tầng thấp đánh đổi an toàn lấy sức mạnh. //go:nosplit, //go:noescape, //go:linkname phá các bảo đảm mà Go dựng lên (stack an toàn, đóng gói, phân tích escape). Dùng sai không báo lỗi lúc biên dịch — nó hỏng lúc chạy, thường theo cách khó lần (hỏng bộ nhớ, tràn stack ngẫu nhiên). Chỉ dùng khi thật hiểu và không có cách khác.
//go:linkname gắn bạn vào nội bộ không ổn định. Hàm runtime không export không có cam kết tương thích — Go có thể đổi tên, chữ ký, hoặc xóa. Code dùng linkname có thể vỡ khi nâng Go. Go 1.23 còn siết linkname với các symbol nội bộ. Tránh trừ khi bắt buộc, và ghi chú rõ để người sau biết rủi ro.
Cạm bẫy khoảng trắng đáng để lint. Vì directive sai vô hiệu âm thầm, một bug dạng này có thể sống lâu (benchmark đo sai, tối ưu không áp dụng). go vet bắt được vài directive đặt sai chỗ, nhưng không phải mọi trường hợp. Khi một directive "không có tác dụng như mong đợi", kiểm cú pháp trước tiên: đúng //go: liền, sát khai báo.
Ba ý mang về
- Compiler directive
//go:là công tắc điều khiển compiler/linker, chia hai nhóm: an toàn hay dùng (//go:embednhúng file lúc build — đo thật in ra nội dung file;//go:generate,//go:noinline) và tầng thấp nguy hiểm (//go:linkname,//go:nosplit,//go:noescape). //go:linknamegọi được symbol không export nhưruntime.nanotime(đo thật dùng nó tính 257000 ns cho vòng lặp 1 triệu, cần importunsafe) — mạnh nhưng phá đóng gói và gắn vào nội bộ runtime có thể đổi bất kỳ phiên bản nào.- Một khoảng trắng thừa sau
//làm directive vô hiệu âm thầm: đo thật// go:noinline(có space) khiến hàm vẫn inline — không lỗi, không cảnh báo; directive phải viết//go:liền và sát khai báo, và luôn kiểm nó có tác dụng thật.
Phần sau ta xem một cơ chế tối ưu hiện đại dùng dữ liệu thực tế: Phần sau mổ xẻ PGO (profile-guided optimization) — cách Go dùng profile CPU từ production để tối ưu tốt hơn, đặc biệt là devirtualization và inlining suy đoán, và đo tác động thật.