Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
tiechui1994 committed May 16, 2024
1 parent 5c69000 commit ebe46fa
Showing 1 changed file with 28 additions and 23 deletions.
51 changes: 28 additions & 23 deletions develop/map/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type mapextra struct {
// 的指针都放在 hmap.extra.overflow 和 hmap.extra.oldoverflow 中了. 注意, 此时的 bmap.overflow 是一个
// uintptr 类型(整数类型), 不会被扫描.
//
// 当 key 和 elem 不包含指针时, 才使用 overflow 和 oldoverflow.
// 当 key 和 elem 不包含指针时, 才会使用 overflow 和 oldoverflow(参考 newoverflow 函数).
// overflow 包含的是 hmap.buckets 的 overflow bucket,
// oldoverflow 包含扩容时的 hmap.oldbuckets 的 overflow bucket.
overflow *[]*bmap
Expand All @@ -51,7 +51,7 @@ type mapextra struct {
nextOverflow *bmap
}
// A bucket for a Go map.
// A bucket for a Go map. 仅仅是一个 header
type bmap struct {
// tophash包含此桶中每个键的哈希值最高字节(高8位)信息(也就是前面所述的high-order bits).
// 如果tophash[0] < minTopHash, tophash[0]则代表桶的搬迁(evacuation)状态.
Expand Down Expand Up @@ -138,15 +138,15 @@ func bmap(t *types.Type) *types.Type {
arr := types.NewArray(types.Types[TUINT8], BUCKETSIZE)
field = append(field, makefield("topbits", arr))
// 2nd filed: keys keytype[BUCKETSIZE]
arr = types.NewArray(keytype, BUCKETSIZE)
arr.SetNoalg(true)
// 2nd filed: keys keytype[BUCKETSIZE]
keys := makefield("keys", arr)
field = append(field, keys)
// 3rd field: elems elemtype[BUCKETSIZE]
arr = types.NewArray(elemtype, BUCKETSIZE)
arr.SetNoalg(true)
// 3rd field: elems elemtype[BUCKETSIZE]
elems := makefield("elems", arr)
field = append(field, elems)
Expand All @@ -165,9 +165,10 @@ func bmap(t *types.Type) *types.Type {
field = append(field, makefield("pad", types.Types[TUINTPTR]))
}
// 如果key和elem都没有指针, 则map实现可以在侧面保留一个 overflow 指针列表, 以便可以将 buckets 标记为没有指针.
// 在这种情况下, 通过将 overflow 字段的类型更改为 uintptr, 使存储桶不包含任何指针.(与前面当中是解释呼应)
// last field: overflow *struct 或 uintptr, 都是 8 字节
// 如果key和elem都没有指针, 则map实现可以在侧面保留一个 overflow 指针列表,
// 以便可以将 buckets 标记为没有指针. 在这种情况下, 通过将 overflow 字段的
// 类型更改为 uintptr, 使存储桶不包含任何指针. (与前面当中是解释呼应)
// 5th field: overflow *struct 或 uintptr, 都是 8 字节
otyp := types.NewPtr(bucket)
if !types.Haspointers(elemtype) && !types.Haspointers(keytype) {
otyp = types.Types[TUINTPTR]
Expand All @@ -181,7 +182,6 @@ func bmap(t *types.Type) *types.Type {
dowidth(bucket)
t.MapType().Bucket = bucket
bucket.StructType().Map = t
return bucket
}
Expand Down Expand Up @@ -292,6 +292,12 @@ func isEmpty(x uint8) bool {
return x <= emptyOne // emptyRest:0, emptyOne:1
}
// 计算 count 对应的 B 是否满足 loadFactor, 最终是寻找一个合适的 B
// 如果 count = 20, 合适的 B 为 2
func overLoadFactor(count int, B uint8) bool {
return count > 8 && uintptr(count) > (bucketShift(B)*6.5)
}
// 获取 b 的 overflow 指针
func (b *bmap) overflow(t *maptype) *bmap {
// 很巧妙, bucketsize 的最后一个 sys.PtrSize 即是 overflow 指针
Expand Down Expand Up @@ -386,7 +392,7 @@ func makemap(t *maptype, hint int, h *hmap) *hmap {
// 如果B为0, 那么buckets字段后续会在mapassign方法中lazily分配
if h.B != 0 {
var nextOverflow *bmap
// makeBucketArray创建一个map的底层保存buckets的数组, 它最少会分配h.B^2的大小.
// makeBucketArray创建一个map的底层保存buckets的数组(连续), 它最少会分配h.B^2的大小.
h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
// 存储 overflow bucket, 当 h.B >= 4 才有(即bucket的数量超过16).
if nextOverflow != nil {
Expand All @@ -409,7 +415,7 @@ func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets un
// 当桶的数量大于等于16个时, 正常情况下就会额外创建2^(b-4)个溢出桶
nbuckets += bucketShift(b - 4)
sz := t.bucket.size * nbuckets // 计算内存大小
up := roundupsize(sz) // 计算mallocgc将分配的内存块的大小(需要以此为准)
up := roundupsize(sz) // 计算mallocgc将分配的内存块的大小, 内存对齐(需要以此为准)
if up != sz {
nbuckets = up / t.bucket.size
}
Expand Down Expand Up @@ -495,7 +501,7 @@ again:
// bucketMask返回值是 2^B-1
// 因此,通过hash值与bucketMask返回值做按位与操作,返回的在buckets数组中的第几号桶
bucket := hash & bucketMask(h.B) // 获取bucket的位置
// 如果map正在扩容(即h.oldbuckets != nil)中, 则先进行搬移工作(当前的bucket).
// 如果map正在扩容 (即h.oldbuckets != nil) 中, 则先进行搬移工作(当前的bucket).
if h.growing() {
growWork(t, h, bucket) // 最多搬移两个bucket
}
Expand All @@ -520,13 +526,11 @@ bucketloop:
// [h0][h1][h2][h3][h4][e][e][e]
// 但在执行过 delete 操作时,可能会变成这样:
// [h0][h1][e][e][h5][e][e][e]
// 所以如果再插入的话,会尽量往前面的位置插
// [h0][h1][e][e][h5][e][e][e]
// ^
// ^
// 这个位置
// 所以在循环的时候还要顺便把前面的空位置先记下来
// 因为有可能在后面会找到相等的key,也可能找不到相等的key
// 这个位置作为插入位置
// 所以在循环的时候还要顺便把前面的空位置先记下来, 首选的位置
// 因为有可能在后面会找到相等的key, 也可能找不到相等的key
if b.tophash[i] != top {
// 如果cell位为空(b.tophash[i] <= emptyOne), 那么就可以在对应位置进行插入
if isEmpty(b.tophash[i]) && inserti == nil {
Expand All @@ -543,7 +547,8 @@ bucketloop:
continue
}
// 第二种情况是cell位的tophash值和当前的tophash值相等
// 第二种情况是cell位的tophash值和当前的 tophash 值相等
// 这种情况还需要继续试探, 如果 key 也是一样, 需要覆盖
// indirectkey() // store ptr to key instead of key itself
// indirectelem() // store ptr to elem instead of elem itself
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
Expand Down Expand Up @@ -579,8 +584,8 @@ bucketloop:
// 在已有的桶和溢出桶中都未找到合适的cell供key写入, 那么有可能会触发以下两种情况
// 情况一:
// 判断当前map的装载因子是否达到设定的6.5阈值, 或者当前map的溢出桶数量是否过多. 如果存在这两种情况之一, 则进行扩容
// 操作.
// 判断当前map的装载因子是否达到设定的6.5阈值, 或者当前map的溢出桶数量是否过多.
// 如果存在这两种情况之一, 则进行扩容操作.
// hashGrow()实际并未完成扩容, 对哈希表数据的搬迁(复制)操作是通过growWork()来完成的.
// 重新跳入again逻辑, 执行两次growWork()操作后, 再次遍历新的桶.
// 分别分析情况1(装载因子) 和 情况2(buckets与overflow buckets)
Expand All @@ -590,8 +595,8 @@ bucketloop:
}
// 情况二:
// 在不满足情况一的条件下, 并且没有找到插入位置, 会为当前桶再新建溢出桶,并将tophash,key插入到新建溢出桶的对应内存
// 的0号位置
// 在不满足情况一的条件下, 并且没有找到插入位置, 会为当前桶再新建溢出桶,
// 并将tophash,key插入到新建溢出桶的对应内存的0号位置
if inserti == nil {
// all current buckets are full, allocate a new one.
newb := h.newoverflow(t, b)
Expand Down Expand Up @@ -651,12 +656,12 @@ func (h *hmap) newoverflow(t *maptype, b *bmap) *bmap {
ovf = (*bmap)(newobject(t.bucket))
}
// 修改 noverflow
// 修改 noverflow 数量
h.incrnoverflow()
// key和value 非指针
if t.bucket.ptrdata == 0 {
h.createOverflow() // 创建 extra 和 overflow
*h.extra.overflow = append(*h.extra.overflow, ovf) // 将 overflow 存储到 extra 当中
*h.extra.overflow = append(*h.extra.overflow, ovf) // 将 overflow 存储到 extra 当中
}
b.setoverflow(t, ovf)
return ovf
Expand Down

0 comments on commit ebe46fa

Please sign in to comment.