goalign

command module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 1 Imported by: 0

README

GoAlign

A fast CLI for analyzing Go struct alignment and rewriting fields into a CPU-friendly order (govet/fieldalignment style, with atomics-first packing).

OpenSSF Scorecard

Module: github.com/gopherust-io/goalign

Latest stable release: see GitHub Releases.

Quick links: Architecture · Usage · Cacheguard · Known limitations · Contributing


Features

  • Struct analysis — detects inter-field and trailing padding waste
  • Autofixgoalign fix rewrites structs to the suggested field order
  • Diff / dry-rungoalign fix --diff prints a unified diff without writing
  • Suggested reorder — atomics / 64-bit counters first, then density packing
  • Suggest policies--policy=atomics|density|stable
  • Cacheguard — detect false sharing; opt-in --cacheguard pads isolate contended fields
  • Layout notesatomics-first, bool-pack, ptrdata, false-share
  • Opt-in bool rewrite--rewrite-bools packs unexported scattered bools into a flags word
  • Opt-in type accuracy--packages resolves imported sizes via go/packages
  • Config file.goalign.yml / goalign.yml project defaults
  • Output formats — text, JSON, table, SARIF (GitHub Code Scanning)
  • go/analysis plugingoalign-analyzer vet tool
  • Ignore — struct/field // goalign:ignore, path globs, generated-file skip
  • Multi-arch--arches=amd64,arm64,386 summary matrix
  • CI gate--fail-on-findings / --min-waste
  • Zero-alloc layout mathCompute / Suggest stay allocation-free with a reused buffer
  • Recursive scan — parallel file analysis

Installation

go install github.com/gopherust-io/goalign@latest

# Optional vet-compatible analyzer
go install github.com/gopherust-io/goalign/cmd/goalign-analyzer@latest

Usage

# Analyze current directory
goalign analyze

# Analyze a file
goalign analyze main.go

# Recursive with excludes (vendor/.git/node_modules/bin are skipped by default)
goalign analyze -r -e testdata/ ./src

# Target a specific GOARCH
goalign analyze --arch=386 ./pkg

# Multi-arch summary
goalign analyze --arches=amd64,arm64,386 -r .

# CI gate: fail if any issue wastes >= 8 bytes
goalign analyze -r --fail-on-findings --min-waste=8 .

# Accurate imported-type sizes (slower)
goalign analyze --packages -r .

# Apply suggested field order (rewrites source; review the diff)
goalign fix -r .
goalign fix --diff -r .          # unified diff only
goalign fix --dry-run --arch=amd64 ./pkg

# Opt-in bool → flags word (unexported bools only; breaking — review carefully)
goalign fix --rewrite-bools --diff ./pkg

# Cacheguard: isolate contended fields across cache lines (may grow the struct)
goalign analyze ./pkg                    # reports false-share notes
goalign fix --cacheguard --diff ./pkg    # preview _cgpadN inserts

# Formats
goalign analyze -f text
goalign analyze -f json
goalign analyze -f table
goalign analyze -f sarif > findings.sarif

# Suggest policy
goalign analyze --policy=density .

CI-friendly gate example:

goalign analyze -r --fail-on-findings --min-waste=8 .

Exit codes: 0 success, 1 analysis errors or findings with --fail-on-findings, 2 usage errors.

Config file

Place .goalign.yml (or goalign.yml) in the project root. Flags override config.

# see goalign.example.yml
arch: amd64
min-waste: 8
fail-on-findings: true
policy: atomics
skip-generated: true
exclude:
  - testdata/
ignore:
  - "**/*.pb.go"
go/analysis / vet
go vet -vettool=$(which goalign-analyzer) ./...

Or import github.com/gopherust-io/goalign/analysis into a multichecker / golangci-lint plugin.

Example

Given:

type BadStruct struct {
    A bool    // 1 byte
    B int64   // 8 bytes (7 bytes padding before)
    C int32   // 4 bytes
    D bool    // 1 byte (+ trailing pad)
}

GoAlign reports waste and a suggested order:

example.go
----------
HIGH BadStruct  line 1
  Struct 'BadStruct' has 10 bytes of padding (41% waste); reorder saves 8 bytes
  Current
    NAME  TYPE   SIZE  OFFSET  ALIGN
    A     bool   1     0       1
    B     int64  8     8       8
    C     int32  4     16      4
    D     bool   1     20      1
  Suggested  (saves 8 bytes)
    NAME  TYPE   SIZE  OFFSET  ALIGN
    B     int64  8     0       8
    C     int32  4     8       4
    A     bool   1     12      1
    D     bool   1     13      1

Summary: 1 issues, 10 bytes wasted, 8 bytes savable

Apply the rewrite:

goalign fix example.go
# Fixed 1 structs in 1 files, saved 8 bytes

goalign fix --diff example.go   # preview without writing

fix rewrites source in place unless --diff / --dry-run is set. Review the diff before committing. Structs marked // goalign:ignore are left unchanged.

Suggest rules

Default policy (--policy=atomics):

  1. Atomics firstint64 / uint64 / atomic.* counters at the start of the struct (keeps 64-bit alignment on 32-bit arches)
  2. Density packing — larger align/size fields next (same idea as govet/fieldalignment)
  3. Bool pack — when 3+ bools are scattered among larger fields, notes a flag-word opportunity (--rewrite-bools can apply an opt-in flags-word rewrite for unexported bools)
  4. ptrdata — notes when a meaningful fraction of the struct is pointer-bearing (GC scan)
  5. Cacheguard / false-share — see Cacheguard below.

Use --policy=density for pure density sort, or --policy=stable to reduce churn (no atomics-first partition).

Cacheguard

Size-only aligners pack fields tightly. Under concurrency that can put two hot counters or a sync.Mutex next to an atomic on the same 64-byte cache line, causing false sharing and hurting throughput. Cacheguard detects that failure mode even when padding waste is already 0.

Other aligners shrink; Cacheguard protects (and may grow sizeof on purpose).

Auto-detect vs annotations
Contended automatically Needs annotation
atomic.* plain int32 / int64 / … → // goalign:contend
sync.Mutex, sync.RWMutex optional // goalign:hot / // goalign:cold hints

Plain int64/uint64 still get atomics-first packing; they are not auto-contended (avoids false-share spam on non-concurrent structs).

Commands
# Notes only (safe default)
goalign analyze examples/cacheguard.go

# Preview Suggested layout with pads (does not write)
goalign analyze --cacheguard examples/cacheguard.go

# Apply pads (review with --diff first)
goalign fix --cacheguard --diff examples/cacheguard.go
goalign fix --cacheguard examples/cacheguard.go

Config (see goalign.example.yml): cacheguard, cache-line (default 64).

Before / after
// Before — 0 padding waste, both atomics on cache line 0
type Hot struct {
    A atomic.Int64
    B atomic.Int64
}

// After goalign fix --cacheguard
type Hot struct {
    A       atomic.Int64
    _cgpad0 [56]byte // goalign:cacheguard — separate contended fields
    B       atomic.Int64
}

Text output includes a CLINE column when false-share notes are present.

Caveats
  • Pads grow the struct; always review --diff before committing.
  • Cacheguard is not a substitute for API/design choices (prefer uncontended ownership when you can).
  • Pad fields are named _cgpadN and marked // goalign:cacheguard (re-runs are idempotent).
  • sync.Mutex / RWMutex sizes are AST heuristics unless --packages is set.

Known limitations

  • Default path uses fast AST heuristics (not full type-checking) for speed.
  • Imported named types are skipped (unknown) unless --packages is set — GoAlign does not guess pointer-sized placeholders for unresolved types.
  • Arrays with unresolvable named constants are skipped to avoid false positives.
  • Autofix reorders fields and may split multi-name declarations (A, B int).
  • --rewrite-bools changes field types (breaking); callers must be updated manually.
  • Per-field // goalign:ignore reports waste for remaining fields but skips autofix.
  • Cacheguard auto-contend is limited to atomic.* and sync.Mutex/RWMutex; plain integers need // goalign:contend.
  • // goalign:hot / cold are soft ordering hints, not a full profile-guided layout.

Ignoring structs and fields

// goalign:ignore
type LegacyStruct struct {
    A bool
    B int64
}

type Partial struct {
    A bool // goalign:ignore
    B int64
    C bool
}

Generated files (Code generated … DO NOT EDIT, *.pb.go, *.gen.go) are skipped by default (--skip-generated=false to include).

Performance

Hot-path layout math

Compute / Suggest (reused 2*n buffer, no notes) are designed for zero heap allocations:

go test ./internal/layout -bench='BenchmarkCompute|BenchmarkSuggest' -benchmem
# BenchmarkCompute-…   0 B/op   0 allocs/op
# BenchmarkSuggest-…   0 B/op   0 allocs/op

make escape   # filtered -gcflags=-m for hot-path packages

Reporting paths (FillTypeNames, messages, AST parse) allocate by design.

Competitive scan

GoAlign optimizes for AST scan speed (no typechecker by default). betteralign / fieldalignment use go/packages and optimize for true type sizes + GC ptrdata. Use --packages when you need import accuracy. Do not treat these as identical work.

Capability goalign betteralign fieldalignment
Autofix yes (fix / --diff) yes (-apply) yes (-fix; often drops comments)
Formats text / json / table / sarif analysis json / diff json
Arch select --arch / --arches host / typesizes host / typesizes
Atomics-first yes (--policy=atomics) no no
bool-pack notes / rewrite notes + opt-in --rewrite-bools no no
Cacheguard / false-share notes + opt-in --cacheguard pads no no
ptrdata / GC notes advisory notes yes yes
Type accuracy (imports) heuristic; --packages for full full full
Ignore directive goalign:ignore (+ field / globs) betteralign:ignore
CI gate --fail-on-findings / --min-waste exit on diagnostics exit on diagnostics

Library path over the shared density+atomics corpus (AnalyzeSource, in-memory):

make bench-corpus
# ~105 µs/op on Apple M4 Pro (directional)

CLI wall-clock on the density tree (warm module cache, Apple M4 Pro sample):

Tool mean ms median ms
goalign analyze -r -f json 16 16
betteralign 56 53
fieldalignment 62 62
make bench-compare   # writes artifacts/benchcmp.md

Corpus: testdata/benchcorpus/. Methodology: scripts/bench-compare.sh.

Building and testing

make test       # unit tests
make test-race  # race detector
make coverage   # coverage gate (COVERAGE_MIN=70)
make bench          # all internal package benchmarks
make bench-corpus   # AnalyzeSource over competitive corpus
make bench-compare  # CLI wall-clock vs betteralign/fieldalignment
make escape         # filtered compiler escape analysis
make fuzz           # fuzz smoke
make ci             # fmt-check + test + race + vet + lint

See CONTRIBUTING.md for the full PR checklist and CI job inventory.

Architecture notes

  • Fast AST heuristics (no go/packages by default) for scan speed; --packages opt-in
  • --arch selects GOARCH size tables (amd64, arm64, 386, arm, mips, riscv64, wasm, …)
  • Handles fixed arrays (including 1<<n lengths), anonymous nested structs, embeds, trailing padding, and trailing zero-sized fields (gc ABI)
  • Unresolvable array lengths (named consts) and unresolved imported types are skipped to avoid false positives

Contributing

License

Apache License 2.0 — see LICENSE.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
Package analysis provides a go/analysis Analyzer wrapping GoAlign heuristics.
Package analysis provides a go/analysis Analyzer wrapping GoAlign heuristics.
cmd
goalign-analyzer command
Command goalign-analyzer is a vet-compatible multichecker for GoAlign.
Command goalign-analyzer is a vet-compatible multichecker for GoAlign.
Package main demos Cacheguard (false-share) findings.
Package main demos Cacheguard (false-share) findings.
internal
alignmath
Package alignmath provides pure struct layout size/alignment arithmetic shared by layout.Compute, Suggest, and nested struct sizing.
Package alignmath provides pure struct layout size/alignment arithmetic shared by layout.Compute, Suggest, and nested struct sizing.
bytesconv
Package bytesconv provides zero-allocation string↔[]byte conversions.
Package bytesconv provides zero-allocation string↔[]byte conversions.
config
Package config loads .goalign.yml project defaults.
Package config loads .goalign.yml project defaults.
diff
Package diff produces unified diffs for goalign fix --diff.
Package diff produces unified diffs for goalign fix --diff.
goarch
Package goarch maps GOARCH names to pointer size and validates known arches.
Package goarch maps GOARCH names to pointer size and validates known arches.
pkgscan
Package pkgscan resolves imported type sizes via go/packages (opt-in --packages).
Package pkgscan resolves imported type sizes via go/packages (opt-in --packages).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL