在sync.Map中加载或存储,而无需每次都创建新结构

时间:2018-08-16 20:46:52

标签: go concurrency

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

这里的用例是,如果我将sync.Map用作高速缓存,其中高速缓存未命中的情况很少(但可能),并且在高速缓存未命中的情况下,我想将其添加到地图中,则需要初始化一个结构每次LoadOrStore都会被调用,而不仅仅是在需要时创建结构。我担心这会对GC造成伤害,会初始化数十万个不需要的结构。

在Java中,可以使用computeIfAbsent完成。

2 个答案:

答案 0 :(得分:2)

  

Package sync

import "sync"
     

type Map

     

地图就像Go地图[interface {}] interface {},但对于   由多个goroutine并发使用,而无需额外的锁定或   协调。加载,存储和删除以摊销常量运行   时间。

     

地图类型是专用的。大多数代码应使用普通的Go地图   相反,具有单独的锁定或协调功能,以提高类型安全性   并使其更易于维护其他不变式和地图   内容。

     

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


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

例如,由于“高速缓存未命中的情况很少发生”,因此假设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
$

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

            }
        }
    }
}

答案 1 :(得分:0)

您可以尝试:

var m sync.Map
s, ok := m.Load("key")
if !ok {
    s, _ = m.LoadOrStore("key", "value")
}

fmt.Println(s)

play demo