Go 语言性能优化技巧

简介: 在Go语言中优化性能涉及数字字符串转换(如用`strconv.Itoa()`代替`fmt.Sprintf()`)、避免不必要的字符串到字节切片转换、预分配切片容量、使用`strings.Builder`拼接、有效利用并发(`goroutine`和`sync.WaitGroup`)、减少内存分配、对象重用(`sync.Pool`)、无锁编程、I/O缓冲、正则预编译和选择高效的序列化方法。这些策略能显著提升代码执行效率和系统资源利用率。

在软件开发中,性能往往是我们需要特别关注的方面之一。对于使用 Go 语言的开发者而言,如何编写高性能的代码是一个重要的考虑点。

今天,我将分享一些在 Go 语言开发中可以采取的性能优化策略,希望能帮助大家写出更高效的程序。

数字与字符串的转换

在处理数字和字符串的转换时,strconv.Itoa() 通常比 fmt.Sprintf() 更加高效。

示例代码

import (
    "fmt"
    "strconv"
)

// 使用 strconv.Itoa() 进行转换
num := 123
str := strconv.Itoa(num)
fmt.Println(str)

// 使用 fmt.Sprintf() 进行转换
str2 := fmt.Sprintf("%d", num)
fmt.Println(str2)

strconv.Itoa() 的速度大约是 fmt.Sprintf() 的两倍,因为后者需要解析格式字符串,而前者直接进行转换。

字符串与字节切片的转换

尽可能避免将 string 转换为 []byte。这个操作会增加额外的内存拷贝,从而影响性能。

处理切片时的性能优化

在使用 for-loopSlice 进行 append 操作时,请先分配足够的容量。

示例代码

// 不推荐的做法
var nums []int
for i := 0; i < 1000; i++ {
   
    nums = append(nums, i)
}

// 推荐的做法
nums := make([]int, 0, 1000) // 预分配足够的容量
for i := 0; i < 1000; i++ {
   
    nums = append(nums, i)
}

通过预先分配足够的容量,可以避免在 append 过程中发生内存重新分配。

字符串的拼接

使用 strings.Builder 对字符串进行拼接,性能远高于使用 ++=

示例代码

import "strings"

// 使用 strings.Builder 进行字符串拼接
var builder strings.Builder
for i := 0; i < 100; i++ {
   
    builder.WriteString("str")
}
result := builder.String()
fmt.Println(result)

并发编程

利用 Go 语言的强大并发特性。使用并发的 goroutine 并配合 sync.WaitGroup 进行同步,可以显著提升程序的执行效率。

示例代码

import (
    "sync"
)

var wg sync.WaitGroup

for i := 0; i < 10; i++ {
   
    wg.Add(1)
    go func(i int) {
   
        defer wg.Done()
        // 你的代码逻辑
    }(i)
}
wg.Wait()

慎用内存分配

避免在性能敏感的代码(热代码)中频繁进行内存分配,以减少垃圾回收的压力。

对象重用

使用 sync.Pool 来重用对象,可以有效降低内存分配的频率和垃圾回收的压力。

pool := sync.Pool{
   
    New: func() interface{
   } {
   
        return new(MyStruct)
    },
}
myVar := pool.Get().(*MyStruct)
// 使用 myVar 后,记得放回池中
pool.Put(myVar)

无锁编程

尽可能采用无锁操作,比如使用 sync/atomic 包中的原子操作,以避免锁的开销。

// 使用 Atomic 包进行原子操作
var counter int64
sync/atomic.AddInt64(&counter, 1)

I/O 缓冲

I/O 操作是非常慢的,使用缓冲 I/O(如 bufio.NewWriter()bufio.NewReader())可以显著提升 I/O 性能。

// 使用 bufio 包进行 I/O 缓冲
writer := bufio.NewWriter(file)
defer writer.Flush()
writer.WriteString("Hello, World")

正则表达式优化

在循环内部使用正则表达式时,应先用 regexp.Compile() 将其编译,以提升性能。

re := regexp.MustCompile("some-regex-pattern")
matches := re.FindAllString("some string", -1)

序列化性能选择

如果对性能有高要求,考虑使用 protobufmsgp 等序列化方案,而不是 json,因为 json 序列化涉及反射,性能较低。

Map 使用技巧

在使用 map 时,整型作为键的性能会比字符串高,这是因为整型的比较操作比字符串比较要快。

// 使用整型作为 Map 的 key
mapInt := map[int]string{
   1: "one", 2: "two"}

总结

  • 如果需要把数字转换成宇符串,使用 strconv.ltoa()fmt.Sprintf() 要快一倍左右。
  • 尽可能避免把 String 转成 []Byte,这个转换会导致性能下降。
  • 如果在 for-loop 里对某个 Slice 使用 append(),请先把 Slice 的容量扩充到位,这样可以避免内存重新分配以及系统自动按 2 的 N 次方幂进行扩展但又用不到的情况,从而避免浪费内存。
  • 使用 StringBuffer 或是 StringBuild 来拼接字符串,性能会比使用 ++= 高三到四个数量级。
  • 尽可能使用并发的 goroutine 然后使用 sync.WaitGroup 来同步分片操作。
  • 避免在热代码中进行内存分配,这样会导致 gc 很忙。
  • 尽可能使用 sync.Pool 来重用对象。
  • 使用 lock-free 的操作,避免使用 mutex,尽可能使用 sync/Atomic 包。
  • 使用 I/O 缓冲,I/O 是个非常非常慢的操作,使用 bufio.NewWrite()bufio.NewReader() 可以带来更高的性能。
  • 对于在 for-loop 里的固定的正则表达式,一定要使用 regexp.Compile() 编译正则表达式。性能会提升两个数量级。
  • 如果你需要更高性能的协议,就要考虑使用 protobufmsgp 而不是 json,因为 json 的序列化和反序列化里使用了反射。
  • 你在使用 Map 的时候,使用整型的 key 会比字符串的要快,因为整型比较比字符串比较要快。

通过上述的技巧,我们可以在编写 Go 程序时更加注重性能。从字符串处理到并发控制,再到内存管理,每一个环节都有提升效率的空间。希望这些技巧能够帮助你在开发过程中写出更高效、更优化的代码。

相关文章
|
24天前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
77 1
|
3月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
262 1
|
3月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
316 0
|
3月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
210 0
|
3月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
177 0
|
3月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
273 0
|
3月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
3月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
4月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
4月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
331 0

热门文章

最新文章