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

简介: 尽量用`time.Time`和`time.Duration`,如果必须用string,尽量用`time.RFC3339`

目录

注:切勿过早优化!

Time

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

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

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

time.Now().Format("2006-01-02 15:04:05")

Performance1

Itoa性能高于Sprint

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

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

// 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

// 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的扩容机制就能很容易地理解。切片越长,影响越大。

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做大量字符串的拼接

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

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操作,这里简单地放个实例:

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

// 如果匹配的格式不会变化,全局只初始化一次即可
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

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

目录
相关文章
|
3月前
|
存储 NoSQL 测试技术
go最佳实践:如何舒适地编码
go最佳实践:如何舒适地编码
|
4月前
|
存储 Go API
一个go语言编码的例子
【7月更文挑战第2天】本文介绍Go语言使用Unicode字符集和UTF-8编码。Go中,`unicode/utf8`包处理编码转换,如`EncodeRune`和`DecodeRune`。`golang.org/x/text`库支持更多编码转换,如GBK到UTF-8。编码规则覆盖7位至21位的不同长度码点。
178 1
一个go语言编码的例子
|
6月前
|
JSON JavaScript 前端开发
Golang深入浅出之-Go语言JSON处理:编码与解码实战
【4月更文挑战第26天】本文探讨了Go语言中处理JSON的常见问题及解决策略。通过`json.Marshal`和`json.Unmarshal`进行编码和解码,同时指出结构体标签、时间处理、omitempty使用及数组/切片区别等易错点。建议正确使用结构体标签,自定义处理`time.Time`,明智选择omitempty,并理解数组与切片差异。文中提供基础示例及时间类型处理的实战代码,帮助读者掌握JSON操作。
170 1
Golang深入浅出之-Go语言JSON处理:编码与解码实战
|
6月前
|
监控 安全 Go
Go语言高效编码:利用上网行为管理软件实现实时警报系统
本文介绍了如何使用Go语言构建一个实时警报系统,用于监控和管理用户上网行为。通过定义监控规则(如使用正则表达式匹配特定网站),程序可以在检测到违规行为时触发警报。利用Go的goroutine实现并发处理,保证系统高效和实时。此外,系统还将警报信息自动提交至网站,便于管理员及时响应,提升网络安全和员工 productivity。
173 9
|
6月前
|
JSON 编解码 Go
【Go语言专栏】Go语言中的JSON编码与解码
【4月更文挑战第30天】Go语言内置JSON编码解码支持,简化了数据交换。`json.Marshal`用于将Go结构体转换为JSON,如示例中`Person`结构体的编码。`json.Unmarshal`则将JSON数据反序列化到结构体,需传入结构体变量的地址。错误处理至关重要,特别是在处理大量数据时,要注意性能优化,如避免不必要的转换和重复操作。了解自定义编码解码和最佳实践能提升开发效率。掌握这些技能,有助于构建高效Go应用。
67 0
|
6月前
|
Go Windows
|
11月前
|
消息中间件 设计模式 缓存
GO 中优雅编码和降低圈复杂度
GO 中优雅编码和降低圈复杂度
|
监控 NoSQL Go
GO 中 ETCD 的编码案例分享
GO 中 ETCD 的编码案例分享
|
设计模式 Kubernetes 监控
Go编程模式 - 8-装饰、管道和访问者模式
装饰、管道和访问者模式的使用频率不高,但在特定场景下会显得很酷
37 0
|
Kubernetes Shell Go
Go编程模式 - 7-代码生成
良好的命名能体现出其价值。尤其是在错误码的处理上,无需再去查询错误码对应的错误内容,直接可以通过命名了解。
65 0