6 Ways To Boost the Performance of Your Go Applications
Source from : https://betterprogramming.pub/6-ways-to-boost-the-performance-of-your-go-applications-5382bb7532d7
1. If your application works in Kubernetes, automatically set GOMAXPROCS to match the Linux container CPU quota
如果你的應用運行在Kubernetes中,自動設置GOMAXPROCS匹配Linux容器CPU配額 在 Kubernetes 環境中運行應用程式時,如果未設置 GOMAXPROCS 參數, Go 語言的調度器可能會使用所有可用的 CPU 核心,導致系統資源過度佔用。 這可能會導致其他應用程式運行緩慢或崩潰,從而影響整個系統的效能。
通過使用https://github.com/uber-go/automaxprocs,Go 調度程序使用的線程數將與您在 k8s yaml 中定義的 CPU 限制一樣多。
Example:
Application CPU limit(Defined in k8s.yaml): 1 core Node core counts: 64
Normally Go scheduler will try to use 64 thread, but if we use automaxprocs it will use only one thread.
2. Sort struct fields (Struct padding)
The order of the fields used in the struct directly affects your memory usage.
type testStruct struct {
testBool1 bool // 1 byte
testFloat1 float64 // 8 bytes
testBool2 bool // 1 byte
testFloat2 float64 // 8 bytes
}
看起來像是會佔用 18 bytes, 但實際上不是
func main() {
a := testStruct{}
fmt.Println(unsafe.Sizeof(a)) // 32 bytes
}
This is because of how memory alignment works internally in a 64-bit architecture. For more information, you can read this article.
How can we decrease? We can sort fields according to memory padding.
type testStruct struct {
testFloat1 float64 // 8 bytes
testFloat2 float64 // 8 bytes
testBool1 bool // 1 byte
testBool2 bool // 1 byte
}
func main() {
a := testStruct{}
fmt.Println(unsafe.Sizeof(a)) // 24 bytes
}
我們不必總是手動對這些字段進行排序。您可以使用諸如fieldalignment.
fieldalignment -fix ./...
3. Garbage collection tune
在 Go 1.19 之前,我們只能 GOGC(runtime/debug.SetGCPercent) 配置 GC 週期;但是,在某些情況下我們會超出內存限制。
在 Go 1.19 中,我們現在擁有 GOMEMLIMIT. GOMEMLIMIT 是一個新的環境變量,允許用戶限制 Go 進程可以使用的內存量。
此功能可以更好地控制 Go 應用程序的內存使用情況,防止它們使用過多內存並導致性能問題或崩潰。
通過設置 GOMEMLIMIT 變量,用戶可以確保他們的 Go 程序平穩高效地運行,而不會對系統造成過度的壓力。
它不會取代 GOGC,而是與其結合使用。
您還可以禁用 GOGC 百分比配置並僅使用 GOMEMLIMIT 來觸發垃圾收集。
GOGC 的值可以影響垃圾回收的頻率和內存分配的行為。 GOGC 值越小,觸發垃圾回收的頻率就越高,從而釋放更多的未使用內存,但是也會導致更多的 CPU 時間花費在垃圾回收上 GC的運行量顯著減少,但您在應用時需要小心。如果您不知道應用程序的限制,請不要設置GOGC=off。
4. Use unsafe package to string <-> byte conversion without copying
在string到byte或byte到string之間轉換時,我們會複製變數。 但在內部,在 Go 中,這兩種類型通常使用StringHeader和SliceHeader值。我們可以在這兩種類型之間進行轉換而無需額外分配
// For Go 1.20 and higher
func StringToBytes(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))
}
func BytesToString(b []byte) string {
return unsafe.String(unsafe.SliceData(b), len(b))
}
// For lower versions
// Check the example here
// https://github.com/bcmills/unsafeslice/blob/master/unsafeslice.go#L116
如果您的 String 或 byte 值以後可能會更改,請不要使用此功能。
5. Use jsoniter instead of encoding/json

Raw Result (easyjson requires static code generation)
| ns/op | allocation bytes | allocation times | |
|---|---|---|---|
| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op |
| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op |
| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op |
| std encode | 2213 ns/op | 712 B/op | 5 allocs/op |
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
Usage
100% compatibility with standard lib
Replace
import "encoding/json"
json.Marshal(&data)
with
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Marshal(&data)
Replace
import "encoding/json"
json.Unmarshal(input, &data)
with
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Unmarshal(input, &data)
6. Use sync.Pool to reduce heap allocations
Sync.Pool 是一個池,可以用於存放和重複使用已經分配的物件,而不是在每次需要時都重新分配新的物件。 這可以減少內存分配和釋放的次數,從而提高效能。 使用 sync.Pool 的方式是將需要重複使用的物件存放在池中,需要時從池中取出,不再需要時放回池中。
以下是一些使用 sync.Pool 的例子:
- 緩存常用的物件。例如,在 HTTP 請求處理程序中,可以使用 sync.Pool 緩存常用的 http.Request 和 http.ResponseWriter 物件。
- 緩存昂貴的資源。例如,在資料庫連接池中,可以使用 sync.Pool 緩存昂貴的資源,例如網路連接和資料庫連接。
- 緩存計算結果。例如,在某些演算法中,可能需要進行大量的中間計算,使用 sync.Pool 可以緩存中間計算結果,減少重複計算的次數。
- 緩存中間狀態。例如,在某些分佈式系統中,可能需要緩存中間狀態以提高效能,使用 sync.Pool 可以緩存中間狀態,減少計算和通訊的負擔。
type Person struct {
Name string
}
var pool = sync.Pool{
New: func() any {
fmt.Println("Creating a new instance")
return &Person{}
},
}
func main() {
person := pool.Get().(*Person)
fmt.Println("Get object from sync.Pool for the first time:", person)
person.Name = "Mehmet"
fmt.Println("Put the object back in the pool")
pool.Put(person)
fmt.Println("Get object from pool again:", pool.Get().(*Person))
fmt.Println("Get object from pool again (new one will be created):", pool.Get().(*Person))
}
//Creating a new instance
//Get object from sync.Pool for the first time: &{}
//Put the object back in the pool
//Get object from pool again: &{Mehmet}
//Creating a new instance
//Get object from pool again (new one will be created): &{}