go1.14实现高性能内存分配器

前言:

go1.14版本中也更新了内存分配器。go先前的版本,在多GOMAXPROCS高频申请内存场景下会引起mheap.lock的锁竞争问题,所以在新版内存分配器着重解决这个问题。

go1.14内存分配器优化的逻辑简单说就是删除自由内存范围的概念,并使用位图跟踪自由内存,另外允许p缓存一部分bitmap。

该文章原文地址 http://xiaorui.cc/archives/6613

为什么mheap成为瓶颈?

虽然在go的内存设计中有多级缓存的概念 ( mcache、mcentral、mheap ),mcache由于绑定在P上,所以无锁的。central数组按照size class芬超了67个,每个mcentral都有一个锁,锁粒度在size class。但mheap结构是全局唯一,相关的内存分配及释放会独占mheap.lock。😅

go1.14内存分配器优化的逻辑简单说就是删除自由内存范围的概念,并使用位图跟踪自由内存,另外在p缓存一部分bitmap。

源码分析

(有空再写) …

go官方给出的内存分配器设计方案,go proposal,https://github.com/golang/proposal/blob/master/design/35112-scaling-the-page-allocator.md

性能测试

测试脚本,逻辑就是开协程不断的申请对象。

// xiaorui.cc

package main

import (
    "fmt"
    "sync"
    "time"
)

type node struct {
    ident   string
    context string
}

func run() {
    iter := 100000
    data := make(map[int]node, iter)
    ts := time.Now().String()

    for i := 0; i < iter; i++ {
        data[i] = node{
            ident: ts,
            context: ts + ts + ts,
        }
    }

    for i := 0; i < iter; i++ {
        delete(data, i)
    }
}


func batchRun() {
    wg := sync.WaitGroup{}

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            run()
            wg.Done()
        }()
    }

    wg.Wait()
}

func main() {
    start := time.Now()
    for i := 0; i < 100; i++ {
        batchRun()
    }

    cost := time.Since(start)
    fmt.Printf("time cost: %+v \n", cost)
}

在并发密集申请内存下,go1.14的内存分配性能是快了一些。(节字大小影响结果)

// xiaorui.cc

go 1.13  vs  go 1.14
time cost: 1m11.458039901s
time cost: 57.242940201s

go 1.13  vs  go 1.14
time cost: 2m27.298913751s
time cost: 1m56.458370589s

cpu消耗相差不大

golang new memory allocator

通过golang pprof top对比,go 1.14的runtime.futex调用少于go1.13。使用strace统计也是同样的结果。


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc