zoukankan      html  css  js  c++  java
  • Go 代码性能优化小技巧

    high-performance

    前言

    本文总结了 Go 语言日常开发中经常使用到的代码性能优化小技巧,每个知识点都可以深挖其原理,但本文只给出结论,并附上 benchmark 结果,从测试数据来直观地看性能差异,具体的原理分析可以看每一小节给出的参考文章。

    前置知识:Go benchmark 详解

    切片预分配容量

    如果我们事先知道数据量大小,则可以提前分配切片容量,避免扩容时的元素拷贝。

    before

    func BenchmarkBefore(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		b.StopTimer()
    		var s []int // 未指定切片容量
    		b.StartTimer()
    		for j := 0; j < 10000; j++ {
    			s = append(s, j)
    		}
    	}
    }
    

    after

    func BenchmarkAfter(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		b.StopTimer()
    		s := make([]int, 0, 1000) // 预先指定切片容量
    		b.StartTimer()
    		for j := 0; j < 10000; j++ {
    			s = append(s, j)
    		}
    	}
    }
    

    benchmark

    $ go test -bench="." -benchmem
    goos: windows
    goarch: amd64
    pkg: learnGolang
    BenchmarkBefore-4          16641             76656 ns/op          386304 B/op         20 allocs/op
    BenchmarkAfter-4           21256             56961 ns/op          334465 B/op          7 allocs/op
    PASS
    ok      learnGolang     6.305s
    

    reference

    Go 切片

    Go slice 扩容机制分析

    大量字符串拼接

    Go 语言的字符串为不可变类型,使用 + 拼接字符串会创建一个新的对象,在大量字符串拼接的场景下,使用 strings.Builder 可以显著提升性能。

    before

    func Before(str string, n int) string {
    	var s string
    	for i := 0; i < n; i++ {
    		s += str // 直接使用 + 来拼接
    	}
    	return s
    }
    

    after

    func After(str string, n int) string {
    	var buf strings.Builder
    	for i := 0; i < n; i++ {
    		buf.WriteString(str) // 使用 strings.Builder 优化拼接
    	}
    	return buf.String()
    }
    

    benchmark

    var (
    	str = "hello,世界"
    	n   = 1000
    )
    
    func BenchmarkBefore(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		Before(str, n)
    	}
    }
    
    func BenchmarkAfter(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		After(str, n)
    	}
    }
    
    $ go test -bench="." -benchmem
    goos: windows
    goarch: amd64
    pkg: learnGolang
    BenchmarkBefore-4            756           1391783 ns/op         6366122 B/op        999 allocs/op
    BenchmarkAfter-4           82411             15351 ns/op           53232 B/op         15 allocs/op
    PASS
    ok      learnGolang     2.705s
    

    reference

    Go string 详解

    字符串拼接性能及原理

    正则表达式预编译

    如果正则表达式是确定不变的,则可以将其定义为全局变量并预先编译,避免每次使用时现编译。

    before

    func ParseIPv4Before(ip string) bool {
    	re := regexp.MustCompile(`(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`)
    	return re.MatchString(ip)
    }
    

    after

    var IPv4Regex = regexp.MustCompile(`(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`)
    
    func ParseIPv4After(ip string) bool {
    	return IPv4Regex.MatchString(ip)
    }
    

    benchmark

    func BenchmarkBefore(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		ParseIPv4Before("192.168.2.255")
    	}
    }
    
    func BenchmarkAfter(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		ParseIPv4After("192.168.2.255")
    	}
    }
    
    
    $ go test -bench="." -benchmem
    goos: windows
    goarch: amd64
    pkg: learnGolang
    BenchmarkBefore-4          80751             12892 ns/op           11046 B/op         83 allocs/op
    BenchmarkAfter-4         3840087               371 ns/op               0 B/op          0 allocs/op
    PASS
    ok      learnGolang     2.992s
    
  • 相关阅读:
    6.Mysql之MGR的限制和局限性
    5.Mysql之MGR原理浅谈02
    2.shell之cut详解
    1.Shell编程的基本语法01
    4.Mysql之MGR浅谈01
    3.Mysql之MHA实战(03)
    2.Mysql之高可用架构MHA(02)
    1.数据库的三大范式是什么?
    1.Mysql之主从复制浅谈01
    6.Mydumper和Myloader备份恢复
  • 原文地址:https://www.cnblogs.com/yahuian/p/go-code-high-performance-tips.html
Copyright © 2011-2022 走看看