美文网首页
golang syn.pool看这篇就够啦

golang syn.pool看这篇就够啦

作者: 护念 | 来源:发表于2023-11-18 18:11 被阅读0次

sync.Pool

背景:
在高并发场景下,短时间内会创建大量临时对象,而这些临时对象都一次性的,创建完就等着被GC(垃圾回收),非常的浪费,也占用了大量的内存,性能不好。

比如,在高并发场景下,会有大量的web请求涌入,在对每个请求响应的过程,会涉及反序列化数据到某某结构体中,这会创建大量临时结构体对象,分配大量内存。

用途:
go为了解决上面的问题,提出了sync.Pool解决方案。
它的作用一句话概括:
复用临时对象,减少内存分配,降低GC压力,提高性能

它的作用看起来和缓存类似,但是不能等效于缓存;

  • 它的数据是由GC决定啥时候回收的,缓存我们可以控制到期时间
  • 它每次获取(Get)后,需要存入(put),缓存多少获取,不会影响下次获取

1. 简要使用

前面我们对sync.Pool有了一个感性的认识,但是还不清楚代码如何写,下面我们看下:

package main

import (
    "encoding/json"
    "fmt"
    "sync"
)

type Student struct {
    Name  string
    Age   int8
    Score [100]byte
}

func main() {
    // 定义一个student临时对象池
    studentPool := sync.Pool{
        // 内部放一个new 的匿名函数
        New: func() interface{} {
            return &Student{}
        },
    }

    // 准备解析数据
    buf, _ := json.Marshal(Student{Name: "张三", Age: 16})

    // 使用
    s := studentPool.Get().(*Student) // 由于是interface{} 取出后转 *Student
    json.Unmarshal(buf, s)
    studentPool.Put(s) // 再此存入 方便下次复用
    fmt.Printf("name: %s, Age: %d\n", s.Name, s.Age)
}

// name: 张三, Age: 16

使用总体分三步:

  1. 初始化一个临时对象池(在New中写好匿名函数)
  2. Get(从池中获取对象,如果池中存在,从池中取,如果不存在通过New函数创建)
  3. Put(放回池中,用完后放回,方便后续使用)

2. 性能测试

上面我们已经知道如何使用,那么他到底有多大的性能提升呢,我们测试下:

// main.go
package main

import (
    "encoding/json"
    "sync"
)

type Student struct {
    Name   string
    Age    int8
    Remark [1024]byte // 这里之所以写的大一点是 因为 sync.Pool对于重性结构体更有效果
}

// 定义一个student临时对象池
var studentPool = sync.Pool{
    // 内部放一个new 的匿名函数
    New: func() interface{} {
        return &Student{}
    },
}

// 准备解析数据
var buf, _ = json.Marshal(Student{Name: "张三", Age: 16})

func main() {
}

// 没有使用池序列化
func UnmarshalWithoutPool() {
    s := &Student{}
    json.Unmarshal(buf, s)
}

// 使用了pool的反序列化
func UnmarshalWithPool() {
    s := studentPool.Get().(*Student)
    json.Unmarshal(buf, s)
    studentPool.Put(s)
}

测试文件:

package main

import "testing"

func BenchmarkUnmarshalWithoutPool(b *testing.B) {
    for n := 0; n < b.N; n++ {
        UnmarshalWithoutPool()
    }
}

func BenchmarkUnmarshalWithPool(b *testing.B) {
    for n := 0; n < b.N; n++ {
        UnmarshalWithPool()
    }
}

运行测试:

dongmingyan@pro ⮀ ~/go_playground/play ⮀ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: example/play
cpu: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
BenchmarkUnmarshalWithoutPool-8             9092            141332 ns/op            1392 B/op          8 allocs/op
BenchmarkUnmarshalWithPool-8                9258            127560 ns/op             240 B/op          7 allocs/op
PASS
ok      example/play    2.809s

上面我们可以看出,在使用pool和未使用pool在耗时上优化不太明显,但在内存占用上差了接近6倍(1392/240 = 5.8);效果还是很明显的;
之所以在时间上不太明显是我们测试的结构体还不够厚重。

3. 使用场景

上面我们测试了性能,那么应该在什么场景下使用呢?

  1. 厚重的结构体上(字段多,字段大)
  2. 高并发场景(频率高,复用率高)

4. 其它

  1. 存在数据丢失(它缓存的数据不稳定,在GC后会丢失,过程不可控)
  2. 由于数据存在丢失,不用能用做类似数据库的缓存,只能存一些丢失后,也不影响使用的临时数据
  3. syn.Pool 线程安全,但是其内部的New函数需要自己实现线程安全

相关文章

  • 备孕?看这篇就够啦!

    结婚是人生大事!无论你是准备结婚还是刚刚结婚,都会幻想过将来宝宝的名字吧,ta长什么样子?跟谁更像呢?身高随爸爸还...

  • Android地理位置这篇就够啦!

    Android 系统提供了地理位置服务相关的API,方便了开发者去获得当前地理位置。用户可以通过GPS接收器接受地...

  • 表情包素材看这篇就够啦

    你有没有这种感觉,自己也算个有趣的人, 但是,手头可怜的表情包,根本不足以支撑自己风骚的内心 每次看别人把表情包用...

  • mysql索引原理,看这篇就够啦

    前言 网上已经有了很多相关mysql索引原理的文章,但是都存在一些问题,有的是直接复制别人的比较老的文章,有的直接...

  • 苏州馄饨攻略 看这篇就够啦

    终于出炉的「苏州馄饨攻略」—— 从吃到写,历时几个月,终于做好啦! 一共20家,有传统菜肉的,也有网红咸蛋黄芝士的...

  • 去泰国怎么吃,收好这篇就够啦!

    去泰国玩,除了普吉、苏梅美丽的岛屿沙滩风光,清迈、拜县清新闲适的小城,一定不能错过的就是极具地方风味的美食啦~~ ...

  • nil,看这篇就够

    写过 Go 代码的人,肯定对下面的代码不陌生: Go 项目中这行代码会大量存在,这里可能隐藏着陷阱。 1. Go ...

  • 期待就够啦

    静待春天的到来,比期待新年的到来更有意义。当然,这是,我非常非常个人色彩的想法。 春天,是一种清新的气味,夹杂着冰...

  • 新生儿肚脐护理,看这篇就够啦!

    宝宝出生三天后,出院了我才发现宝宝肚脐上还有个伤口,要怎么护理呢?需要消毒吗? 宝宝的肚脐上怎么变黑了? 宝宝出生...

  • 还担心不会挑纸尿裤?看这篇就够啦!

    如何为宝贝挑选纸尿裤?不少新手宝妈都有这样的困惑。纸尿裤每天与宝贝的接触时间长达24小时,只有选对了好产品,才能让...

网友评论

      本文标题:golang syn.pool看这篇就够啦

      本文链接:https://www.haomeiwen.com/subject/uohpwdtx.html