memory - 为什么使用单独的线程原子操作更快?

标签 memory go concurrency cpu cpu-cache

我有两段代码,桌面上有32个内核。

代码 A 使用 32 个线程并执行以下操作,

1) 将值写入内存中的一些随机位置 2) 原子地向全局变量添加一个值。

代码 B 使用 16 个线程将值写入随机位置,并使用另外 16 个线程以原子方式将值添加到全局变量。

我想知道为什么代码 B 在每秒对全局变量执行多少原子操作方面更快。

这里是代码A

var a uint64 = 0

const N = 10 * 1024 * 1024

var data [N]uint64

func main() {

    for i := 0; i < 32; i ++ {
        go func(id int) {
            source := rand.NewSource(int64(id))
            local_rand := rand.New(source)
            for {
                atomic.AddUint64(&a, uint64(1))
                data[local_rand.Int31n(N)] = uint64(1)
            }
        }(i)

    }

    var b uint64 = 0

    for {
        c := atomic.LoadUint64(&a)

        fmt.Println(c - b)
        b = c
        time.Sleep(time.Second)
    }

}

这里是代码B

var a uint64 = 0

const N = 10 * 1024 * 1024

var data [N]uint64

func main() {

    for i := 0; i < 16; i ++ {
        go func(id int) {
            source := rand.NewSource(int64(id))
            local_rand := rand.New(source)
            for {
                data[local_rand.Int31n(N)] = uint64(1)
            }
        }(i)

    }
    for i := 0; i < 16; i++ {

        go func() {
            for {
                atomic.AddUint64(&a, uint64(1))
            }

        }()
    }
    var b uint64 = 0

    for {
        c := atomic.LoadUint64(&a)

        fmt.Println(c - b)
        b = c
        time.Sleep(time.Second)
    }

}

最佳答案

原子操作是昂贵的。为了保持原子性,您需要确保该代码块的互斥执行。通常这是使用加载链接、存储条件等指令来实现的。在代码 A 中,您有 32 个原子操作,但在代码 B 中,您只有 16 个。原子工作更少,执行时间更短!

作为实验,尝试使用代码 A 中的内存操作更改原子操作的顺序。(只需先执行内存,然后再执行原子操作)。您应该会看到执行时间有所减少。

关于memory - 为什么使用单独的线程原子操作更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42309055/

相关文章:

C++ 调用析构函数并没有真正从内存中删除对象

linux munmap 不工作(或者至少不能立即工作)

c++ - 如何释放重新分配的内存? C++

memory - CUDA 表面与纹理

go - Golang 中 strings.Contains 和 strings.ContainsAny 的区别

arrays - 试图添加到 slice 的索引超出范围

java - Java中的单线程查询

html - 嵌入数据或不嵌入数据什么是服务/解析动态内容的最佳实践

java - 使用可变键映射并发

java - 是否有可能在 Java 中有效地实现 seqlock?