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

Commit 9cc0eb1

Browse files
author
Stephen Gutekanst
authored
Merge pull request #192 from sourcegraph/sg/mem
significantly lower memory consumption for vscode-go users
2 parents af3cc41 + dfc81e3 commit 9cc0eb1

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,21 @@ go get github.com/sourcegraph/go-langserver
1919
| | Hover | Jump to def | Find references | Workspace symbols | VFS extension | Isolated | Parallel |
2020
|----|-------|-------------|-----------------|-------------------|---------------|----------|----------|
2121
| Go | x | x | x | x | x | x | x |
22+
23+
## Profiling
24+
25+
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:
26+
27+
Capture a heap (memory) profile:
28+
29+
```bash
30+
go tool pprof -svg $GOPATH/bin/go-langserver http://localhost:6060/debug/pprof/heap > heap.svg
31+
```
32+
33+
Capture a CPU profile:
34+
35+
```bash
36+
go tool pprof -svg $GOPATH/bin/go-langserver http://localhost:6060/debug/pprof/profile > cpu.svg
37+
```
38+
39+
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).

main.go

+56
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ import (
77
"io"
88
"log"
99
"net"
10+
"net/http"
1011
"os"
12+
"runtime/debug"
13+
"time"
1114

1215
"github.com/sourcegraph/go-langserver/langserver"
1316
"github.com/sourcegraph/jsonrpc2"
17+
18+
_ "net/http/pprof"
1419
)
1520

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

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

42+
// Start pprof server, if desired.
43+
if *pprof != "" {
44+
go func() {
45+
log.Println(http.ListenAndServe(*pprof, nil))
46+
}()
47+
}
48+
49+
if *freeosmemory {
50+
go freeOSMemory()
51+
}
52+
3553
if err := run(); err != nil {
3654
fmt.Fprintln(os.Stderr, err)
3755
os.Exit(1)
@@ -106,3 +124,41 @@ func (stdrwc) Close() error {
106124
}
107125
return os.Stdout.Close()
108126
}
127+
128+
// freeOSMemory should be called in a goroutine, it invokes
129+
// runtime/debug.FreeOSMemory() more aggressively than the runtime default of
130+
// 5 minutes after GC.
131+
//
132+
// There is a long-standing known issue with Go in which memory is not returned
133+
// to the OS aggressively enough[1], which coincidently harms our application
134+
// quite a lot because we perform so many short-burst heap allocations during
135+
// the type-checking phase.
136+
//
137+
// This function should only be invoked in editor mode, not in sourcegraph.com
138+
// mode, because users running the language server as part of their editor
139+
// generally expect much lower memory usage. In contrast, on sourcegraph.com we
140+
// can give our servers plenty of RAM and allow Go to consume as much as it
141+
// wants. Go does reuse the memory not free'd to the OS, and as such enabling
142+
// this does _technically_ make our application perform less optimally -- but
143+
// in practice this has no observable effect in editor mode.
144+
//
145+
// The end effect of performing this is that repeating "hover over code" -> "make an edit"
146+
// 10 times inside a large package like github.com/docker/docker/cmd/dockerd:
147+
//
148+
//
149+
// | Real Before | Real After | Real Change | Go Before | Go After | Go Change |
150+
// |-------------|------------|-------------|-----------|----------|-----------|
151+
// | 7.61GB | 4.12GB | -45.86% | 3.92GB | 3.33GB | -15.05% |
152+
//
153+
// Where `Real` means real memory reported by OS X Activity Monitor, and `Go`
154+
// means memory reported by Go as being in use.
155+
//
156+
// TL;DR: 46% less memory consumption for users running with the vscode-go extension.
157+
//
158+
// [1] https://github.com/golang/go/issues/14735#issuecomment-194470114
159+
func freeOSMemory() {
160+
for {
161+
time.Sleep(1 * time.Second)
162+
debug.FreeOSMemory()
163+
}
164+
}

0 commit comments

Comments
 (0)