将两个值存储在一个变量中。

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

Store 2 values in one variable

问题

这是Go代码正确且可移植的吗?我需要将两个计数器(每次调用只更新一个计数器)存储在一个变量中,以避免在实际代码中使用单个atomic.AddUint64()时锁定整个结构体。

package main

import "fmt"

var long uint64 // 实际计数器存储

func main() {
    left := uint32(100) // 第一个计数器
    right := uint32(200) // 第二个计数器
    long = uint64(left)
    long = long << 32 | uint64(right)
    fmt.Println(left, right)

    long += uint64(1 << 32) // 增加左计数器
    long += 1 // 增加右计数器

    xLeft := uint32(long >> 32) // 获取左计数器
    xRight := uint32(long) // 获取右计数器
    fmt.Println(xLeft, xRight)
}

你可以在这里查看代码:http://play.golang.org/p/aBlp-Zatgn

英文:

Is this Go code correct and portable, I need to store 2 counters (each call only one counter will be updated) in one variable to avoid locks in actual code where I am going to use single atomic.AddUint64() instead of locking whole struct.

<!-- language: lang-golang -->

package main

import &quot;fmt&quot;

var long uint64 // Actual counters storage

func main() {
    left := uint32(100) // First counter
	right := uint32(200) // Second counter
    long = uint64(left)
	long = long &lt;&lt; 32 | uint64(right)
    fmt.Println(left, right)

	long += uint64(1 &lt;&lt; 32) // Increment left
    long += 1 // Increment right

	xLeft := uint32(long &gt;&gt; 32) // Get left
    xRight := uint32(long) // Get right
	fmt.Println(xLeft, xRight)
}

http://play.golang.org/p/aBlp-Zatgn

答案1

得分: 0

这段Go代码是正确的,只要你使用64位宽度的无符号整数。

在这种情况下,通过sync/atomic包提供了对Go编译器支持的体系结构的可移植性。然而,请注意,并非所有体系结构都支持对64位宽度数据进行“真正”的原子操作。例如,i386实现使用了CAS循环:

TEXT ·AddUint64(SB),NOSPLIT,$0-20
    // 没有XADDQ,所以使用CMPXCHG8B循环
    MOVL    addr+0(FP), BP
    TESTL   $7, BP
    JZ  2(PC)
    MOVL    0, AX // crash with nil ptr deref
    // DI:SI = delta
    MOVL    delta_lo+4(FP), SI
    MOVL    delta_hi+8(FP), DI
    // DX:AX = *addr
    MOVL    0(BP), AX
    MOVL    4(BP), DX
addloop:
    // CX:BX = DX:AX (*addr) + DI:SI (delta)
    MOVL    AX, BX
    MOVL    DX, CX
    ADDL    SI, BX
    ADCL    DI, CX

    // if *addr == DX:AX {
    //  *addr = CX:BX
    // } else {
    //  DX:AX = *addr
    // }
    // all in one instruction
    LOCK
    CMPXCHG8B    0(BP)

    JNZ addloop

    // success
    // return CX:BX
    MOVL    BX, new_lo+12(FP)
    MOVL    CX, new_hi+16(FP)
    RET

这可能会引发一个问题:为什么不使用带有锁的结构体?

编辑以回答评论中的问题:是的,使用32位整数将在所有Go支持的体系结构上实现实际的原子操作,因为据我所知,它们都支持XADDL(或类似操作)。

英文:

> Is this Go code correct and portable

It is correct, as long as you work with unsigned integers of 64-bit width.

Portability in this case is provided by the sync/atomic packages to the architectures supported by the Go compiler. Note, however, that not all architectures support "true" atomic operations on 64-bit wide data. For example, the i386 implementation uses a CAS-loop:

TEXT &#183;AddUint64(SB),NOSPLIT,$0-20
	// no XADDQ so use CMPXCHG8B loop
	MOVL	addr+0(FP), BP
	TESTL	$7, BP
	JZ	2(PC)
	MOVL	0, AX // crash with nil ptr deref
	// DI:SI = delta
	MOVL	delta_lo+4(FP), SI
	MOVL	delta_hi+8(FP), DI
	// DX:AX = *addr
	MOVL	0(BP), AX
	MOVL	4(BP), DX
addloop:
	// CX:BX = DX:AX (*addr) + DI:SI (delta)
	MOVL	AX, BX
	MOVL	DX, CX
	ADDL	SI, BX
	ADCL	DI, CX

	// if *addr == DX:AX {
	//	*addr = CX:BX
	// } else {
	//	DX:AX = *addr
	// }
	// all in one instruction
	LOCK
	CMPXCHG8B	0(BP)

	JNZ	addloop

	// success
	// return CX:BX
	MOVL	BX, new_lo+12(FP)
	MOVL	CX, new_hi+16(FP)
	RET

That may open the question: why not use a struct with a lock?


Edit to answer question in comments: Yes, using a 32-bit integer would result in actual atomic operations on all Go-supported architectures because they all support XADDL (or analog) to the best of my knowledge.

huangapple
  • 本文由 发表于 2015年5月22日 15:48:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/30391141.html
匿名

发表评论

匿名网友

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

确定