go: runtime: select is not fair

What version of Go are you using (go version)?

$ go version
go version go1.9 linux/amd64

Does this issue reproduce with the latest release?

yes, on 1.9

What operating system and processor architecture are you using (go env)?

GOARCH=“amd64” GOBIN=“” GOEXE=“” GOHOSTARCH=“amd64” GOHOSTOS=“linux” GOOS=“linux” GOPATH=“/home/bchenebault/DEV/git-oab.si.fr.intraorange/BU900102/ceo-be” GORACE=“” GOROOT=“/usr/local/go” GOTOOLDIR=“/usr/local/go/pkg/tool/linux_amd64” GCCGO=“gccgo” CC=“gcc” GOGCCFLAGS=“-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build913166031=/tmp/go-build -gno-record-gcc-switches” CXX=“g++” CGO_ENABLED=“1” CGO_CFLAGS=“-g -O2” CGO_CPPFLAGS=“” CGO_CXXFLAGS=“-g -O2” CGO_FFLAGS=“-g -O2” CGO_LDFLAGS=“-g -O2” PKG_CONFIG=“pkg-config”

What did you do?

A Go select statement with reflect package function reflect.Select(...) should block until at least one of the cases can proceed and make a uniform pseudo-random choice. It appears that the behavior has changed since returned values are no longer pseudo-random in specific cases only.

Our case is the following : 2 signaling channels preceding 2 “logic” channels https://play.golang.org/p/0TiMtsRk03

But the same version with only one channel preceding the 2 others works https://play.golang.org/p/sy4RjEEjdg

This case perfectly works on go1.8.3 and below.

What did you expect to see?

A pseudo random distribution

What did you see instead?

A strange distribution, surely not pseudo random

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 33 (29 by maintainers)

Commits related to this issue

Most upvoted comments

Yes, xorshift on ARM is basically 3 instructions thanks to barrel shift, hard to beat. I still think that 1ns in fastrand is not going to have any real world impact, though.

Is 1ns slower important for fastrand? Does it ever use a noticeable amount of cpu in a hot loop? Xorshift is among the fastest with good properties; you can come up with endless number of faster prngs with worse properties, but I fear we might end up in a similar bug some day.

@rasky , xorshift is slower. Otherwise it is good because it gives almost same randomness for high and low bits.

I have other variant for fastrandn:

func fastrandn(n uint32) uint32 {
    // some constants with random bit pattern
    // (this are from hash32.go)
    const m1 = 3168982561
    const m2 = 3339683297
    fr := fastrand()
    fr ^= m1
    fr *= m2
    return uint32(uint64(fr) * uint64(n) >> 32)
}

It also gives fair distribution

100192 100322  99857 100461
 99535 100347 100216 100209
 99533 100023  99899 100376
 99678  99796  99982  99574

And it is faster than xorshift. But it uses multiplication, and doesn’t fix cases when raw fastrand used.

https://play.golang.org/p/29n_4OfVQq

What about using xorshift instead?

var state uint32 = 2463534242

func fastrand() uint32 {
	fr := state
	fr ^= fr<<13
	fr ^= fr>>17
	fr ^= fr<<5
	state = fr
	return fr
}

Outputs:

 62797  62420  62588  62303 
 62401  62408  61990  62399 
 62207  62601  62742  62515 
 62916  62716  62655  62342 

(https://play.golang.org/p/sD9jmKGWJy)