Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

significantly lower memory consumption for vscode-go users #192

Merged
merged 3 commits into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,21 @@ go get github.com/sourcegraph/go-langserver
| | Hover | Jump to def | Find references | Workspace symbols | VFS extension | Isolated | Parallel |
|----|-------|-------------|-----------------|-------------------|---------------|----------|----------|
| Go | x | x | x | x | x | x | x |

## Profiling

If you run into performance issues while using the language server, it can be very helpful to attach a CPU or memory profile with the issue report. To capture one, first [install Go](https://golang.org/doc/install) and then:

Capture a heap (memory) profile:

```bash
go tool pprof -svg $GOPATH/bin/go-langserver http://localhost:6060/debug/pprof/heap > heap.svg
```

Capture a CPU profile:

```bash
go tool pprof -svg $GOPATH/bin/go-langserver http://localhost:6060/debug/pprof/profile > cpu.svg
```

Since these capture the active resource usage, it's best to run these commands while the issue is occuring (i.e. while memory or CPU is high).
56 changes: 56 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import (
"io"
"log"
"net"
"net/http"
"os"
"runtime/debug"
"time"

"github.com/sourcegraph/go-langserver/langserver"
"github.com/sourcegraph/jsonrpc2"

_ "net/http/pprof"
)

var (
Expand All @@ -19,6 +24,8 @@ var (
trace = flag.Bool("trace", false, "print all requests and responses")
logfile = flag.String("logfile", "", "also log to this file (in addition to stderr)")
printVersion = flag.Bool("version", false, "print version and exit")
pprof = flag.String("pprof", ":6060", "start a pprof http server (https://golang.org/pkg/net/http/pprof/)")
freeosmemory = flag.Bool("freeosmemory", true, "aggressively free memory back to the OS")
)

// version is the version field we report back. If you are releasing a new version:
Expand All @@ -32,6 +39,17 @@ func main() {
flag.Parse()
log.SetFlags(0)

// Start pprof server, if desired.
if *pprof != "" {
go func() {
log.Println(http.ListenAndServe(*pprof, nil))
}()
}

if *freeosmemory {
go freeOSMemory()
}

if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand Down Expand Up @@ -106,3 +124,41 @@ func (stdrwc) Close() error {
}
return os.Stdout.Close()
}

// freeOSMemory should be called in a goroutine, it invokes
// runtime/debug.FreeOSMemory() more aggressively than the runtime default of
// 5 minutes after GC.
//
// There is a long-standing known issue with Go in which memory is not returned
// to the OS aggressively enough[1], which coincidently harms our application
// quite a lot because we perform so many short-burst heap allocations during
// the type-checking phase.
//
// This function should only be invoked in editor mode, not in sourcegraph.com
// mode, because users running the language server as part of their editor
// generally expect much lower memory usage. In contrast, on sourcegraph.com we
// can give our servers plenty of RAM and allow Go to consume as much as it
// wants. Go does reuse the memory not free'd to the OS, and as such enabling
// this does _technically_ make our application perform less optimally -- but
// in practice this has no observable effect in editor mode.
//
// The end effect of performing this is that repeating "hover over code" -> "make an edit"
// 10 times inside a large package like github.com/docker/docker/cmd/dockerd:
//
//
// | Real Before | Real After | Real Change | Go Before | Go After | Go Change |
// |-------------|------------|-------------|-----------|----------|-----------|
// | 7.61GB | 4.12GB | -45.86% | 3.92GB | 3.33GB | -15.05% |
//
// Where `Real` means real memory reported by OS X Activity Monitor, and `Go`
// means memory reported by Go as being in use.
//
// TL;DR: 46% less memory consumption for users running with the vscode-go extension.
//
// [1] https://github.com/golang/go/issues/14735#issuecomment-194470114
func freeOSMemory() {
for {
time.Sleep(1 * time.Second)
debug.FreeOSMemory()
}
}