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

huangapple go评论63阅读模式
英文:

why atomic operation is faster using separate threads?

问题

我有两段代码,我的桌面上有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)
    }

}
英文:

I have two pieces of code and there are 32 cores on my desktop.

Code A uses 32 threads and does the following thing,

  1. write values to some random locations in memory
  2. atomically add a value to a global variable.

Code B uses 16 threads to write values to random locations and uses another 16 threads to atomically add a value to a global variable.

I was wondering why code B is faster in terms of how many atomic operation executed per second to the global variable.

Here is code A

var a uint64 = 0

const N = 10 * 1024 * 1024

var data [N]uint64

func main() {

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

    }

    var b uint64 = 0

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

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

}

Here is code B

var a uint64 = 0

const N = 10 * 1024 * 1024

var data [N]uint64

func main() {

    for i := 0; i &lt; 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 &lt; 16; i++ {

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

	    }()
    }
    var b uint64 = 0

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

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

}

答案1

得分: 1

原子操作是昂贵的。为了保持原子性,你需要确保该代码块的互斥执行。通常使用类似于load-linked、store-conditional等指令来实现。在你的代码A中,有32个原子操作,但在代码B中,只有16个。原子操作越少,执行时间越短!

作为一个实验,尝试在代码A中改变原子操作与内存操作的顺序(先执行内存操作,然后再执行原子操作)。你应该会看到执行时间的减少。

英文:

Atomic operations are expensive. In order to maintain atomicity, you need to ensure mutually exclusive execution for that block of code. Usually this is achieved using instructions like load-linked, store-conditional etc. In your code A, you have 32 atomic operations, but in code B, you only got 16. Less atomic work, reduced execution time!

As an experiment, try changing order of the atomic operations with the memory operation in your code A.( Just do memory first and atomic afterwards). You should see a reduction in execution time.

huangapple
  • 本文由 发表于 2017年2月18日 07:18:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/42309055.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定