slice扩容


扩容的源码
func growslice(et *_type, old slice, cap int) slice {
...
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else { if old.len < 1024 { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = cap } } } ... switch { case et.size == 1: lenmem = uintptr(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) case et.size == sys.PtrSize: lenmem = uintptr(old.len) * sys.PtrSize newlenmem = uintptr(cap) * sys.PtrSize capmem = roundupsize(uintptr(newcap) * sys.PtrSize) overflow = uintptr(newcap) > maxAlloc/sys.PtrSize newcap = int(capmem / sys.PtrSize) case isPowerOfTwo(et.size): var shift uintptr if sys.PtrSize == 8 { // Mask shift for better code generation. shift = uintptr(sys.Ctz64(uint64(et.size))) & 63 } else { shift = uintptr(sys.Ctz32(uint32(et.size))) & 31 } lenmem = uintptr(old.len) << shift newlenmem = uintptr(cap) << shift capmem = roundupsize(uintptr(newcap) << shift) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) capmem = roundupsize(capmem) newcap = int(capmem / et.size) } }
 
  
package main

import (
    "fmt"
    "unsafe"
)

type ss struct {
    ptr unsafe.Pointer
    l   int
    c   int
}

type A struct {
    f bool
    b int32
    a int
    c int64
    d string
}

func main() {
    Af()
    return
}

func Af() {
    a := make([]A, 1, 1)
    b := A{}

    fmt.Println("Sizeof A:", unsafe.Sizeof(b))

    p1 := (*ss)(unsafe.Pointer(&a))
    fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1)
    a = append(a, A{}, A{}, A{}, A{})
    fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1)

    for i := 0; i < 2048; i++ {
        a = append(a, A{})
        fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1)
    }

}

部分输出

Sizeof A: 40

len=1,cap=1,ptr=0xc00005c420

len=5,cap=5,ptr=0xc00005c420

len=6,cap=10,ptr=0xc00005c420

。。。

len=1638,cap=1638,ptr=0xc00005c420

len=1639,cap=2048,ptr=0xc00005c420

。。。


扩容分析
1、a = append(a, A{}, A{}, A{}, A{})
增加4个元素
原容量是1,元素个数增加了4个,不够存储需要扩容
newcap := old.cap //old.cap=>1
...
doublecap := newcap + newcap //2
if cap > doublecap {//cap=>5 实际的容量是5,大于2,新容量取值5
  newcap = cap
}
//Sizeof A: 40
数据类型的size是40个字节
走switch的
default:
  lenmem = uintptr(old.len) * et.size
  newlenmem = uintptr(cap) * et.size
  capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) //这里capmem=et.size*newcap即40*5=200
  //    14        208        8192       39          80      8.12%

  capmem = roundupsize(capmem)//内存对齐向上取整 为208
  newcap = int(capmem / et.size) //208/40=5
  新容量为5
  len=5,cap=5,ptr=0xc00005c420

  2、看一下由1638扩容到2048的情况
  先看结果
  

  len=1638,cap=1638,ptr=0xc00005c420

  len=1639,cap=2048,ptr=0xc00005c42

  分析:

  数据类型size=40

 
    newcap := old.cap //先取用old.cap =>1638
    doublecap := newcap + newcap //1638+1638=3276
    if cap > doublecap { //cap是元素实际个数为1639
        newcap = cap
    } else {
        if old.len < 1024 {//old.len=1638大于1024
            newcap = doublecap
        } else {
            // Check 0 < newcap to detect overflow
            // and prevent an infinite loop.
            for 0 < newcap && newcap < cap { //符合这个条件,扩容到1.25倍=>2047 
                newcap += newcap / 4
            }
            // Set newcap to the requested cap when
            // the newcap calculation overflowed.
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
....

default:
        lenmem = uintptr(old.len) * et.size
        newlenmem = uintptr(cap) * et.size
        capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) //capmem=2047*40 =>81880
        capmem = roundupsize(capmem) //内存对齐取整到81920
        newcap = int(capmem / et.size) //81920/40=2048

//取整代码
func roundupsize(size uintptr) uintptr {
    if size < _MaxSmallSize {
        if size <= smallSizeMax-8 {
            return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])
        } else {
            return uintptr(class_to_size[size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]])
        }
    }
    if size+_PageSize < size {
        return size
    }
    return round(size, _PageSize) //=>81920
}

func round(n, a uintptr) uintptr {
    return (n + a - 1) &^ (a - 1)
}


size是81880大于_MaxSmallSize =32768 //源码中定义的常量

_PageShift      = 13
_PageSize = 1 << _PageShift  //1<<13  =>8192

 


 

 参考链接

https://www.jianshu.com/p/303daad705a3

转载于:https://www.cnblogs.com/sui-z/p/10458814.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值