golang中降本增效的常规实践

83f68e1cfdbcad340af696d905db8029.gif

最近一年各大中小厂都在搞"优化",说到优化,目的还是"降本增效",降低成本,增加效益(效率)。

技术层面,也有一些降本增效的常规操作。

比如池化、io缓冲区技术


golangC#eg.
池化技术snnc.PoolObjectPool前端切图仔,归入前端资源池 , 随用随取
字节数组缓冲区bytes.BufferList---
io缓冲区bufioBufferStream适度超前,赛道埋伏
池化技术 sync.Pool

sync.Pool位于标准库,该文件提供了对临时对象的重复使用能力, 避免了频繁的gc, 对并发协程是安全的。

该文件只有三个控制点:

  • • New: 默认的临时对象

  • • Get:从池中哪一个临时对象

  • • Put:放回池中,以重用

下面使用基准测试进行b.N*1000次运算时的内存消耗。

package main

import (
    "sync"
    "testing"
)

type Person struct {
    Age int
}

var (
    personPool = sync.Pool{
        New: func() interface{} {  // 设置默认值
            return &Person{}
        },
    }
)

func ExampleObjPool() {
    var p *Person
    for i := 0; i < 1000; i++ {
        for j := 0; j < 1000; j++ {
            p = personPool.Get().(*Person)
            p.Age = i + 1
            personPool.Put(p)
        }
    }
    p = personPool.Get().(*Person)
    fmt.Println(p.Age)
    // output:1000
}

func BenchmarkWithoutPool(b *testing.B) {
    var p *Person
    b.ReportAllocs()
    b.ResetTimer()

    for i := 0; i < b.N; i++ {
        for j := 0; j < 1000; j++ {
            p = new(Person)    // 每次均产生临时对象
            p.Age = 23
        }
    }
}

func BenchmarkWithPool(b *testing.B) {
    var p *Person
    b.ReportAllocs()
    b.ResetTimer()

    for i := 0; i < b.N; i++ {
        for j := 0; j < 1000; j++ {
            p = personPool.Get().(*Person)  // 从池中复用对象
            p.Age = 23
            personPool.Put(p)     // 放回以重用
        }
    }
}

测试结果如下,sync.Pool[重用临时对象]的性能可见一斑。

ff680d656bcfdd2a82082ce173b56ccd.png
bytes.Buffer

golang很多方法内充斥了[]byte, 就连最常规的序列化/反序列化,返回值/参数都是[]byte, 但是slice是一个冷冰冰的数据结构,没有得心趁手的操作行为,还有很多陷阱。

func Marshal(v any) ([]byte, error)
  func Unmarshal(data []byte, v any)

A bytes.Buffer is a variable-sized buffer of bytes with Read and Write methods.
坦白讲bytes.Buffer并非底层优化机制, 实际提供了一个友好操作slice的方式。 

下面的"abcd"字符串,先读取首字符、后面追加字符"e":

var b bytes.Buffer
    b.Write([]byte("abcd")) // 写入之后,自动扩容
    rdbuf := make([]byte, 1)
    _, err := b.Read(rdbuf) // 读取一个字节的数据,移动读off指针
    if err != nil {
        panic(err)
    }
    fmt.Println(b.String()) // 上面读取了一个字符,读off已经移动,现从读off位置转换为string
    b.WriteByte('e')        // 在尾部写字符
    fmt.Println(b.String())
    fmt.Printf("%d, %d \n", b.Len(), b.Cap()) // Len方法返回还能读取的字符数量,Cap返回底层buf的容量
  
//output:
bcd 
bcde
4, 64
a24a7d210c972d35902cb8fe851e02a5.png
io缓冲区 bufio

Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O.

25f430eceab8eeda8dc63cc5a70abb41.png

首先我们需要知道当应用程序执行IO操作(从文件、网络和数据库读取或写入数据),它将触发底层的系统调用,从性能角度来看,这很繁重。

缓冲IO是一种技术,用于在传递之前暂时积累IO操作的结果。这种技术可以通过减少系统调用的次数来提高程序的速度

例如,如果您想要逐字节地从磁盘读取数据,与每次直接从磁盘读取每个字节相比,使用缓冲区IO技术,我们可以一次将一个数据块读入缓冲区,然后消费者可以以任何您想要的方式从缓冲区读取数据。通过减少繁重的系统调用,性能将得到提高。

磁盘:1.寻址:ms(毫秒) 2.磁盘带宽[1]:MB/s
内存:1.寻址:ns(纳秒) 2. 内存带宽[2]:GB/s
磁盘比内存在寻址上慢了10W倍,传输带宽上慢了20倍。

开源的带缓冲区的logrus日志写入hook[3],就利用了bufio技术。

// 利用bufio针对原始io.Writer封装成带缓冲区的io.Writer  
 `s.writer = bufio.NewWriterSize(s.Writer, size) 
  ......
  if len(bs) > s.writer.Available() && s.writer.Buffered() > 0 {
        if err := s.writer.Flush(); err != nil {
            return err
        }
    }
    _, err = s.writer.Write(bs)`
优化总结
  • • sync.Pool 复用临时对象,减少内存分配和gc次数

  • • bufio利用缓冲区,减少笨重的系统调用

  • 其实就是“降本增效”的体现。

  • b16cf24eba940e62992f4514a2f0727f.gif

  • 本文图文原创,欢迎斧正。耗时3h,有用指数5颗星。

引用链接

[1] 磁盘带宽: https://it.sohu.com/a/580279682_121118998
[2] 内存带宽: https://baike.baidu.com/item/%E5%86%85%E5%AD%98%E5%B8%A6%E5%AE%BD
[3] 带缓冲区的logrus日志写入hook: https://github.com/zwbdzb/logrus-bufferedWriter-hook

2717731300194d0232d0ed2256bc2335.gif

自古以来,同步/异步都是八股文第一章

自古以来,反射也是兵家必争之地

Go编程快闪之logrus日志库

流量调度、微服务可寻址性和注册中心

摸鱼快报:golang net/http中的雕虫小技

Go语言正/反向代理的姿势

两将军问题和TCP三次握手

点“3a2972db5e4923f26cbbaf3a63195feb.gif戳“在看a9dca97b3950ee99cb696e08d242640d.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有态度的马甲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值