Skip to content

dkorunic/betteralign

Repository files navigation

betteralign

GitHub license GitHub release go-recipes

About

betteralign is a tool that detects structs that would use less memory if their fields were reordered, and optionally rewrites them.

It is a fork of the official Go fieldalignment tool, with the bulk of the alignment logic unchanged. Notable differences include:

  • skips generated files, identified either by a known suffix (_generated.go, _gen.go, .gen.go, .pb.go, .pb.gw.go) or by a package-level comment containing Code generated by ... DO NOT EDIT.,
  • skips test files (_test.go suffix),
  • skips structs annotated with // betteralign:ignore,
  • supports opt-in mode, where only structs annotated with // betteralign:check are checked,
  • preserves comments (field comments, doc comments, and floating comments) — though comment position heuristics remain a work in progress,
  • performs atomic file writes to prevent corruption or data loss on rewrite (not on Windows),
  • includes more thorough tests comparing expected versus golden output,
  • adapts automatically to CPU and memory constraints in containerized environments (Docker, Kubernetes, LXC, LXD, etc.).

Comment preservation is achieved using DST (Decorated Syntax Tree), which attaches comment decorations to AST nodes. The downside is that the entire file must be reprinted to retain those decorations — partial rewrites via SuggestedFixes are not possible. This is why the -fix flag from the analysis package has no effect, and why integration with golangci-lint is not supported.

Go's standard AST does not associate comments with nodes — it only stores byte offsets, so the original fieldalignment tool erases all comments. A proposed fix exists as an open CL but has not yet been merged.

Note: This is a single-pass tool. Achieving a fully optimal layout may require running it more than once.

This tool builds upon the following prior work:

Deep dive

betteralign pursues two goals:

  1. Minimize struct size by sorting fields in descending alignment order, reducing internal padding between fields.
  2. Reduce GC pointer scan overhead by grouping pointer-bearing fields before pointer-free ones (the GC stops scanning at the last pointer in a value).

With those goals in mind, fields are sorted stably by the following criteria, in order:

  1. Zero-sized types first.
  2. Higher alignment first.
  3. Pointer-bearing types before pointer-free types.
  4. Among pointer-bearing types, those with fewer trailing non-pointer bytes first (minimizing GC ptrdata).
  5. Larger size first.

Consider the following example:

package foo

type IPAddr struct {
	Zone string // 16 bytes, 8-byte aligned
	IP   []byte // 24 bytes, 8-byte aligned
}

Zone comes first because string has fewer trailing non-pointer bytes than []byte: Sizeof(string) - ptrdata(string) = 8 versus Sizeof([]byte) - ptrdata([]byte) = 16.

Installation

Manual: Download the appropriate binary from the releases page and place it in your PATH, typically /usr/local/bin/betteralign.

Via go install:

go install github.com/dkorunic/betteralign/cmd/betteralign@latest

Usage

betteralign: find structs that would use less memory if their fields were sorted

Usage: betteralign [-flag] [package]

This analyzer find structs that can be rearranged to use less memory, and provides
a suggested edit with the most compact order.

Note that there are two different diagnostics reported. One checks struct size,
and the other reports "pointer bytes" used. Pointer bytes is how many bytes of the
object that the garbage collector has to potentially scan for pointers, for example:

        struct { uint32; string }

have 16 pointer bytes because the garbage collector has to scan up through the string's
inner pointer.

        struct { string; *uint32 }

has 24 pointer bytes because it has to scan further through the *uint32.

        struct { string; uint32 }

has 8 because it can stop immediately after the string pointer.

Be aware that the most compact order is not always the most efficient.
In rare cases it may cause two variables each updated by its own goroutine
to occupy the same CPU cache line, inducing a form of memory contention
known as "false sharing" that slows down both goroutines.

Unlike most analyzers, which report likely mistakes, the diagnostics
produced by betteralign very rarely indicate a significant problem,
so the analyzer is not included in typical suites such as vet or
gopls. Use this standalone command to run it on your code:

   $ go install github.com/dkorunic/betteralign/cmd/betteralign@latest
   $ betteralign [packages]



Flags:
  -V    print version and exit
  -all
        no effect (deprecated)
  -apply
        apply suggested fixes
  -c int
        display offending line with this many lines of context (default -1)
  -cpuprofile string
        write CPU profile to this file
  -debug string
        debug flags, any subset of "fpstv"
  -diff
        with -fix, don't update the files, but print a unified diff
  -exclude_dirs value
        exclude directories matching a pattern
  -exclude_files value
        exclude files matching a pattern
  -fix
        apply all suggested fixes
  -flags
        print analyzer flags in JSON
  -generated_files
        also check and fix generated files
  -json
        emit JSON output
  -memprofile string
        write memory profile to this file
  -opt_in
        opt-in mode on per-struct basis with 'betteralign:check' in comment
  -source
        no effect (deprecated)
  -tags string
        no effect (deprecated)
  -test
        indicates whether test files should be analyzed, too (default true)
  -test_files
        also check and fix test files
  -trace string
        write trace log to this file
  -v    no effect (deprecated)

To check all packages in the current module:

betteralign ./...

To automatically rewrite files (excluding test and generated files):

betteralign -apply ./...

Generated and test files can be included with the -generated_files and -test_files flags respectively. Use -exclude_dirs and -exclude_files to skip specific paths. Use -opt_in to check only structs explicitly annotated with // betteralign:check.

Star history

Star History Chart

About

Make your Go programs use less memory (maybe)

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages