LoadOrStore 在sync.Map 中,无需每次创建新结构

Sha*_*aun 6 concurrency go

是否可以LoadOrStore进入 Go 而sync.Map无需每次都创建新的结构?如果没有,有哪些替代方案?

这里的用例是,如果我使用作为sync.Map缓存,其中缓存未命中很少(但可能),并且在缓存未命中时我想添加到映射中,我需要在每次LoadOrStore调用时初始化一个结构,而不仅仅是在需要时创建结构。我担心这会损害 GC,初始化数十万个不需要的结构。

在 Java 中,这可以使用computeIfAbsent.

pet*_*rSO -1

包同步

import "sync"
Run Code Online (Sandbox Code Playgroud)

类型地图

Map 类似于 Go 的 map[interface{}]interface{},但可以安全地由多个 goroutine 并发使用,无需额外的锁定或协调。加载、存储和删除在分摊常量时间内运行。

Map 类型是专门的。大多数代码应该使用普通的 Go 映射,并具有单独的锁定或协调,以获得更好的类型安全性,并更容易维护其他不变量以及映射内容。

Map 类型针对两种常见用例进行了优化:(1) 当给定键的条目仅写入一次但读取多次时,如在只会增长的缓存中,或者 (2) 当多个 goroutine 读取、写入和读取时覆盖不相交的键集的条目。在这两种情况下,与与单独的 Mutex 或 RWMutex 配对的 Go Map 相比,使用 Map 可以显着减少锁争用。


解决这些问题的通常方法是构建使用模型,然后对其进行基准测试。

例如,由于“缓存未命中很少见”,因此假设它将Load在大部分时间工作并且仅LoadOrStore在必要时(通过值分配和初始化)工作。

$ go test map_test.go -bench=. -benchmem
BenchmarkHit-4     2     898810447 ns/op        44536 B/op        1198 allocs/op
BenchmarkMiss-4    1    2958103053 ns/op    483957168 B/op    43713042 allocs/op
$
Run Code Online (Sandbox Code Playgroud)

map_test.go:

package main

import (
    "strconv"
    "sync"
    "testing"
)

func BenchmarkHit(b *testing.B) {
    for N := 0; N < b.N; N++ {
        var m sync.Map
        for i := 0; i < 64*1024; i++ {
            for k := 0; k < 256; k++ {

                // Assume cache hit
                v, ok := m.Load(k)
                if !ok {
                    // allocate and initialize value
                    v = strconv.Itoa(k)
                    a, loaded := m.LoadOrStore(k, v)
                    if loaded {
                        v = a
                    }
                }
                _ = v

            }
        }
    }
}

func BenchmarkMiss(b *testing.B) {
    for N := 0; N < b.N; N++ {
        var m sync.Map
        for i := 0; i < 64*1024; i++ {
            for k := 0; k < 256; k++ {

                // Assume cache miss
                // allocate and initialize value
                var v interface{} = strconv.Itoa(k)
                a, loaded := m.LoadOrStore(k, v)
                if loaded {
                    v = a
                }
                _ = v

            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 然而,这并没有回答问题。如何在 Go 中进行computeIfAbsent - 具体来说,仅在地图中缺少该值时才创建新值? (3认同)