Introduction:
- The premise of performance optimization is to meet quality factors such as correctness, reliability, simplicity and clarity
- Performance optimization is a comprehensive evaluation, and sometimes time efficiency and space efficiency may be opposed
Performance Optimization Suggestions – Benchmark
The go language provides support for benchmark performance testing, how to use it has to be checked
slice
-
Slice pre-allocates memory, providing capacity information when initializing the slice with make() as much as possible
data := make([]int,0) data := make([]int,0,10)
-
The underlying implementation of slices is a scalable linked list
-
Gotcha: Large memory not freed
- Creates a slice on top of an existing slice, without creating a new underlying array
- The original slice is larger, and the code creates a new small slice based on the original slice
- The original underlying array is referenced in memory and cannot be released
-
Copy can be used instead of re-slice
func GetLastBySlice(origin []int) []int{ return origin[len(origin) - 2:] } // 下面这个节省的空间更多 fun getLastByCopy(origin []int) []int{ result := make([]int,2) copy(result,origin[len(origin) - 2:]) return result }
map
- map pre-allocates memory
- The operation of continuously adding new elements to the map will trigger the expansion of the map
- Allocating space in advance can reduce memory copy and Rehash consumption
- It is recommended to estimate the required space in advance according to the actual needs
strings.Buider for string processing
- The performance of using + splicing is the worst, strings.Buider, bytes.Buffer are similar, strings. Buffer is faster
- When the string is in go, it is an immutable type and occupies a fixed size
- Using + will reallocate memory every time
- Both strings.Buider and bytes.Buffer use []byte array at the bottom
Use empty structs to save memory
Use the atomic package
-
The realization of the lock is realized by the operating system, which belongs to the system call, and the call cost is very high
-
Atomic operations are implemented by hardware, which is more efficient than locks
-
sync.Mutex should be used to protect a piece of logic,. Not just for protecting a variable
-
For non-value operations, you can use atomic.Value, which can carry an interface{}
type atomicCounter struct{ i int32 } func AtomicAddOne(c *atomicCounter){ atomic.AddInt32(&c.i,1) } type mutexCounter struct{ i int32 m sync.Mutex } func MutexAddOne(c *mutexCounter){ c.m.Lock() c.i++ c.m.UnLock() }
summary:
- Avoiding common performance pitfalls can guarantee the functionality of most programs
- Ordinary application code, don't blindly pursue the performance of the program
- The more advanced the performance optimization method, the more prone to problems
- Improve program performance under the premise of meeting correct, reliable, concise and clear quality requirements