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

时间:2017-02-17 23:18:19

标签: memory go concurrency cpu cpu-cache

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

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

1)将值写入内存中的某些随机位置 2)以原子方式向全局变量添加值。

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

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

这是代码A

SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%' :
+--------------------------+-----------------+
| Variable_name            | Value           |
+--------------------------+-----------------+
| character_set_client     | utf8            |
| character_set_connection | utf8            |
| character_set_database   | utf8            |
| character_set_filesystem | binary          |
| character_set_results    | utf8            |
| character_set_server     | utf8            |
| character_set_system     | utf8            |
| collation_connection     | utf8_general_ci |
| collation_database       | utf8_unicode_ci |
| collation_server         | utf8_unicode_ci |
+--------------------------+-----------------+

这是代码B

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)
    }

}

1 个答案:

答案 0 :(得分:1)

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

作为一个实验,尝试使用代码A中的内存操作来改变原子操作的顺序。(先做内存然后再做原子操作)。你应该看到执行时间缩短了。