前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go编程模式 - 2.基础编码下

Go编程模式 - 2.基础编码下

作者头像
junedayday
发布2021-08-05 14:51:56
2630
发布2021-08-05 14:51:56
举报
文章被收录于专栏:Go编程点滴

注:本文的灵感来源于GOPHER 2020年大会陈皓的分享,原PPT的链接可能并不方便获取,所以我下载了一份PDF到git仓,方便大家阅读。我将结合自己的实际项目经历,与大家一起细品这份文档。

目录

  • 时间格式
  • 性能1
  • 性能2
  • 扩展阅读

注:切勿过早优化!

Time

这部分的内容实战项目中用得不多,大家记住耗子叔总结出来的一个原则即可:

尽量用time.Timetime.Duration,如果必须用string,尽量用time.RFC3339

然而现实情况并没有那么理想,实际项目中用得最频繁,还是自定义的2006-01-02 15:04:05

代码语言:javascript
复制
time.Now().Format("2006-01-02 15:04:05")

Performance1

Itoa性能高于Sprint

主要性能差异是由于Sprint针对的是复杂的字符串拼接,底层有个buffer,会在它的基础上进行一些字符串的拼接;

Itoa直接通过一些位操作组合出字符串。

代码语言:javascript
复制
// 170 ns/op
func Benchmark_Sprint(b *testing.B) {
 for i := 0; i < b.N; i++ {
  _ = fmt.Sprint(rand.Int())
 }
}

// 81.9 ns/op
func Benchmark_Itoa(b *testing.B) {
 for i := 0; i < b.N; i++ {
  _ = strconv.Itoa(rand.Int())
 }
}

减少string到byte的转换

主要了解go的string[]byte的转换还是比较耗性能的,但大部分情况下无法避免这种转换。

我们注意一种场景即可:从[]byte转换为string,再转换为[]byte

代码语言:javascript
复制
// 43.9 ns/op
func Benchmark_String2Bytes(b *testing.B) {
 data := "Hello world"
 w := ioutil.Discard
 for i := 0; i < b.N; i++ {
  w.Write([]byte(data))
 }
}

// 3.06 ns/op
func Benchmark_Bytes(b *testing.B) {
 data := []byte("Hello world")
 w := ioutil.Discard
 for i := 0; i < b.N; i++ {
  w.Write(data)
 }
}

切片能声明cap的,尽量初始化时声明

了解slice的扩容机制就能很容易地理解。切片越长,影响越大。

代码语言:javascript
复制
var size = 1000

// 4494 ns/op
func Benchmark_NoCap(b *testing.B) {
 for n := 0; n < b.N; n++ {
  data := make([]int, 0)
  for k := 0; k < size; k++ {
   data = append(data, k)
  }
 }
}

// 2086 ns/op
func Benchmark_Cap(b *testing.B) {
 for n := 0; n < b.N; n++ {
  data := make([]int, 0, size)
  for k := 0; k < size; k++ {
   data = append(data, k)
  }
 }
}

避免用string做大量字符串的拼接

频繁拼接字符串的场景并不多,了解即可。

代码语言:javascript
复制
var strLen = 10000

// 0.0107 ns/op
func Benchmark_StringAdd(b *testing.B) {
 var str string
 for n := 0; n < strLen; n++ {
  str += "x"
 }
}

// 0.000154 ns/op
func Benchmark_StringBuilder(b *testing.B) {
 var builder strings.Builder
 for n := 0; n < strLen; n++ {
  builder.WriteString("x")
 }
}

// 0.000118 ns/op
func Benchmark_BytesBuffer(b *testing.B) {
 var buffer bytes.Buffer
 for n := 0; n < strLen; n++ {
  buffer.WriteString("x")
 }
}

Performance2

并行操作用sync.WaitGroup控制

热点内存分配用sync.Pool

注意一下,一定要是热点,千万不要 过早优化

倾向于使用lock-free的atomic包

除了常用的CAS操作,还有atomic.ValueStoreLoad操作,这里简单地放个实例:

代码语言:javascript
复制
func main() {
 v := atomic.Value{}
 type demo struct {
  a int
  b string
 }

 v.Store(&demo{
  a: 1,
  b: "hello",
 })

 data, ok := v.Load().(*demo)
 fmt.Println(data, ok)
 // &{1 hello} true
}

复杂场景下,还是建议用mutex

对磁盘的大量读写用bufio包

bufio.NewReader()bufio.NewWriter()

对正则表达式不要重复compile

代码语言:javascript
复制
// 如果匹配的格式不会变化,全局只初始化一次即可
var compiled = regexp.MustCompile(`^[a-z]+[0-9]+$`)

func main() {
 fmt.Println(compiled.MatchString("test123"))
 fmt.Println(compiled.MatchString("test1234"))
}

用protobuf替换json

go项目内部通信尽量用protobuf,但如果是对外提供api,比如web前端,json格式更方便。

map的key尽量用int来代替string

代码语言:javascript
复制
var size = 1000000

// 0.0442 ns/op
func Benchmark_MapInt(b *testing.B) {
 var m = make(map[int]struct{})
 for i := 0; i < size; i++ {
  m[i] = struct{}{}
 }
 b.ResetTimer()
 for n := 0; n < size; n++ {
  _, _ = m[n]
 }
}

// 0.180 ns/op
func Benchmark_MapString(b *testing.B) {
 var m = make(map[string]struct{})
 for i := 0; i < size; i++ {
  m[strconv.Itoa(i)] = struct{}{}
 }
 b.ResetTimer()
 for n := 0; n < size; n++ {
  _, _ = m[strconv.Itoa(n)]
 }
}

示例中strconv.Itoa函数对性能多少有点影响,但可以看到stringint的差距是在数量级的。

Further

PPT中给出了8个扩展阅读,大家根据情况自行阅读。

如果说你的时间只够读一个材料的话,我推荐大家反复品读一下Effective Go

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go编程点滴 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • Time
  • Performance1
    • Itoa性能高于Sprint
      • 减少string到byte的转换
        • 切片能声明cap的,尽量初始化时声明
          • 避免用string做大量字符串的拼接
          • Performance2
            • 并行操作用sync.WaitGroup控制
              • 热点内存分配用sync.Pool
                • 倾向于使用lock-free的atomic包
                  • 对磁盘的大量读写用bufio包
                    • 对正则表达式不要重复compile
                      • 用protobuf替换json
                        • map的key尽量用int来代替string
                        • Further
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档