Skip to content

Demo/experimental tools for playing with LSP logging output from Go's compiler, gc.

License

Notifications You must be signed in to change notification settings

dr2chase/gc-lsp-tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gc-lsp-tools

gclsp_prof reads the Go compiler's LSP-encoded logging of places where an optimization was not possible, and combines that with profiling data to see if you might be able to improve performance by tweaking your code slightly. The use of profiling data is to avoid overwhelming users with noise about all the not-performance-critical places that the optimizer left a check in or was forced to do a heap allocation, etc.

To install:

go get -u github.com/dr2chase/gc-lsp-tools/cmd/gclsp_prof

If you have an existing Go benchmark that exercises the code you'd like to measure, you can generate and display the missed (or not possible) optimizations at hotspots directly. For example:

# Get a benchmark if you don't have one of your own
git clone https://github.com/dr2chase/gc-lsp-tools'
cd gc-lsp-tools/cmd/gclsp_prof/testdata
# 
gclsp_prof -bench=Bench -keep=bar 

and you should see

gclsp_prof -bench=Bench -keep=bar 
( go test -gcflags=-json=0,$PWD/bar.lspdir -cpuprofile=$PWD/bar.prof -bench=Bench . )
goos: darwin
goarch: amd64
pkg: github.com/dr2chase/gc-lsp-tools/cmd/gclsp_prof/testdata
BenchmarkDo-4   	       1	2403061102 ns/op
PASS
ok  	github.com/dr2chase/gc-lsp-tools/cmd/gclsp_prof/testdata	2.684s

  1.2%, $PWD/foo_test.go:77)
            (inline) $PWD/foo_test.go:15
        escapes, x escapes to heap (at line 77)
                (inline-earlier )  $PWD/foo_test.go:14
        isInBounds (at line 77)
                (inline)  $PWD/foo_test.go:15
  1.6%, $PWD/foo_test.go:33)
        escapes, x escapes to heap (at line 33)
                (inline-nearby )  $PWD/foo_test.go:14
        isInBounds (at line 33)
                (inline-nearby )  $PWD/foo_test.go:15
        isInBounds (at line 33)
                (inline-nearby )  $PWD/foo_test.go:11
  2.5%, $PWD/foo_test.go:33)
            (inline) $PWD/foo_test.go:11
        escapes, x escapes to heap (at line 33)
                (inline)  $PWD/foo_test.go:14
        isInBounds (at line 33)
                (inline)  $PWD/foo_test.go:15
        isInBounds (at line 33)
                (inline)  $PWD/foo_test.go:11

The two files bar.lspdir and bar.prof can be used as inputs to rerun the command, perhaps with different parameters, for example gclsp_prof -t=0.5 -b=1 bar.lspdir bar.prof

If you would like to do this to an application rather than a benchmark, there are additional steps.

First, be sure that your application supports profiling (as Go test and benchmarks do). Example code to do this:

        file, _ := os.Create("foo.prof")
        pprof.StartCPUProfile(file)

        // Insert your interesting code here

        pprof.StopCPUProfile()
        file.Close()

Second, compile some or all of your Go (1.14) application with LSP logging enabled:

go build -gcflags=-json=0,$PWD/foo.lspdir myapp.go

or (all of your application, likely to pick up some GC and runtime code too)

go build -gcflags=all=-json=0,$PWD/foo.lspdir myapp.go

The -json option normally wants an absolute path, and the zero is a version number. The compilation-logging source code explains the flags, directory structure and format for data stored in $PWD/foo.lspdir (but you don't need to know this to use this tool).

Then run your application, which will create a profile, for example foo.prof.

Finally, run, for example

gclsp_prof -t=5.0 $PWD/foo.lspdir foo.prof

This will produce output that looks something like:

  7.4%, $PWD/foo.go:65)
            (inline) $PWD/foo.go:20
        isInBounds (at line 65)
                (inline)  $PWD/foo.go:20
        isInBounds (at line 65)
                (inline)  $PWD/foo.go:20
 20.4%, $PWD/foo.go:94)
            (inline) $PWD/foo.go:20
        isInBounds (at line 94)
                (inline)  $PWD/foo.go:20
 33.4%, $PWD/foo.go:38)
            (inline) $PWD/foo.go:20
        isInBounds (at line 38)
                (inline-earlier )  $PWD/foo.go:16

Inline locations in the list above appear on lines following a sample line, prefixed with (inline).

To generate the sample data above and also see the commands involved, in github.com/dr2chase/gc-lsp-tools/cmd/gclsp_prof, run go test -v . -here which should produce foo.lspdir and foo.prof for testdata/foo.go.

Possibly useful options include:

  • -a=N, mention compiler diagnostics from N lines after a hot spot (default 0).
  • -b=N, mention compiler diagnostics from N lines before a hot spot (default 0).
  • -t=N.F, (a float) samples less hot than the threshold percentage are ignored (default 1.0).
  • -e, for diagnostics with extended explanations (escape analysis soon), also show the extended explanations.
  • -bench=Bench..., if not empty, run "go test -bench=Bench...." with the additional flags necessary to generate the lsp information and profile, then run gclsp_prof on those with the other flags.
  • -packages=packagePattern, collect diagnostics for the listed packages (default is local directory, see go help packages)
  • -keep=basename, for -bench, put the lsp and profile files in $PWD/basename.{lspdir,prof}
  • -s=ev1,ev2,..., list of environment variables to use to shorten paths. Default "PWD,GOROOT,GOPATH,HOME".
  • -cpuprofile=file, because every application should have this option.
  • -v, verbose. You don't want verbose.

About

Demo/experimental tools for playing with LSP logging output from Go's compiler, gc.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages