Exposing math/rand's lockedSource

802 views
Skip to first unread message

jasdel

unread,
Oct 12, 2015, 2:00:19 PM10/12/15
to golang-dev
I noticed math/rand/rand.go's lockedSource is not published, and I think it would make sense for a constructor of it to be. The default global rand functions all take advantage of the locked source's concurrency safety, but use a fixed seed. If I wanted to take advantage of a locked source, with a custom seed I would need to copy the lockedSource from rand.go, or implement my own. It is not a lot of code to write by hand, but it seems out of place to need to copy it verbatim from the stdlib's source.

I propose math/rand add a new function "NewLockedSource(seed inte64) Source" similar to "NewSource" so developers could have easy access to a concurrency safe rand.Source with custom seed.

I wasn't able to find any previous discussion on this issue, so apologies if I missed them, nor was this idea mentioned in the original code review of lockedSource.

cheers,
Jason

Evan Shaw

unread,
Oct 12, 2015, 2:18:32 PM10/12/15
to jasdel, golang-dev
I've had this same proposal sitting in my drafts for a long time because I couldn't decide whether or not it was worth it.

Anyway, this is some code I've copied into more than one code base, so I would be happy to see it exported from math/rand.

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dave Cheney

unread,
Oct 12, 2015, 2:21:59 PM10/12/15
to Evan Shaw, jasdel, golang-dev

Why do you want to share a random source? Is it because obtaining a new properly seeded random source is too complicated? If that is the case. It would seem better to solve that problem instead.

Thanks

Dave

Matthew Sackman

unread,
Oct 12, 2015, 2:26:02 PM10/12/15
to golang-dev
On Mon, Oct 12, 2015 at 06:21:47PM +0000, Dave Cheney wrote:
> Why do you want to share a random source? Is it because obtaining a new
> properly seeded random source is too complicated? If that is the case. It
> would seem better to solve that problem instead.

rng := rand.New(rand.NewSource(time.Now().UnixNano()))

FWIW, I've consistently found the global one causes perf limitations
because of the global locking, so I pretty much end up with one rng per
go-routine (which incidentally is what Erlang does too). Just use some
const in there instead of time if you want the whole thing to still be
deterministic.

Matthew

Rob Pike

unread,
Oct 12, 2015, 2:45:15 PM10/12/15
to golang-dev
See also https://go-review.googlesource.com/#/c/10161/. If I can resolve some questions about the default generator, it will make it much cheaper to have many independent sources.

-rob


jasdel

unread,
Oct 12, 2015, 3:18:44 PM10/12/15
to golang-dev, eds...@gmail.com, delp...@gmail.com
My use case is probably not a common one. In it, a single global random source is used to add jitter to request retry delays. Performance is not critical here, and it was simpler to use a global random source instead of a source per request, since requests can be made from an unknown number of goroutines. The performance isn't critical, because the random source is only used for failed requests prior to sleeping before being retried.

A custom seed (time.Now()) during init is used just to keep the jitter non-deterministic. Alternatively I could switch to the global default seeded rand funcs since the randomality of the jitter isn't that critical.

Cheers,
Jason

Evan Shaw

unread,
Oct 12, 2015, 3:27:20 PM10/12/15
to golang-dev
> rng := rand.New(rand.NewSource(time.Now().UnixNano()))
>
> FWIW, I've consistently found the global one causes perf limitations
> because of the global locking, so I pretty much end up with one rng per
> go-routine (which incidentally is what Erlang does too). Just use some
> const in there instead of time if you want the whole thing to still be
> deterministic.

An RNG per goroutine is great if you have a nice place to stash the
RNG. If you don't, though, you either need to create one every time
you need one or make a pool, which probably requires a global lock
anyway.

Creating a new RNG every time you need one is expensive unless you can
use it for a long time to amortize the cost.

Rob's new x/exp/rand package is an improvement, but it looks like
creating a new Rand would still require at least two allocations.

- Evan

Andrew Gerrand

unread,
Oct 12, 2015, 7:53:25 PM10/12/15
to Evan Shaw, golang-dev

On 13 October 2015 at 06:26, Evan Shaw <eds...@gmail.com> wrote:
If you don't, though, you either need to create one every time
you need one or make a pool, which probably requires a global lock
anyway.

sync.Pool was more or less designed for this.

Evan Shaw

unread,
Oct 12, 2015, 8:51:16 PM10/12/15
to Andrew Gerrand, golang-dev
What I was trying to say was that sync.Pool is not better in all cases
and lockedSource can still be useful. Under low contention, a
lockedSource beats a sync.Pool.

Although I just ran some quick benchmarks and lockedSource's victory
margin looks pretty small under low contention, so it's probably not a
good choice in general.

tpu...@google.com

unread,
Feb 22, 2018, 9:24:57 PM2/22/18
to golang-dev
This proposal was never accepted or rejected, but it remains relevant.  I'd like to resuscitate it.

In the original thread, Dave Cheney questioned why anyone would want a shared random source.  The use case that brought me here seems common: I have an object that's supposed to be thread-safe and has a method that needs to generate random numbers.  Because the method may be called from multiple goroutines, I can't use a Source returned by rand.NewSource.  But in testing my object, I want to use dependency injection, so I can't use the default locked source.

I'd be happy to create a pull request adding "NewLockedSource(seed inte64) Source" if people think this is reasonable.

Dave Cheney

unread,
Feb 22, 2018, 9:33:06 PM2/22/18
to tpu...@google.com, golang-dev
I think this discussion predated the proposal process. I recommend
raising your issue as a proposal, https://golang.org/issue/new, before
writing any code.

https://github.com/golang/proposal#readme

fwiw. I still don't think this is a good idea.

On 23 February 2018 at 12:58, tpudlik via golang-dev

Josh Bleecher Snyder

unread,
Feb 22, 2018, 10:27:35 PM2/22/18
to tpu...@google.com, golang-dev
On Thu, Feb 22, 2018 at 6:24 PM tpudlik via golang-dev <golan...@googlegroups.com> wrote:
This proposal was never accepted or rejected, but it remains relevant.  I'd like to resuscitate it.

In the original thread, Dave Cheney questioned why anyone would want a shared random source.  The use case that brought me here seems common: I have an object that's supposed to be thread-safe and has a method that needs to generate random numbers.  Because the method may be called from multiple goroutines, I can't use a Source returned by rand.NewSource.  But in testing my object, I want to use dependency injection, so I can't use the default locked source.

I'd be happy to create a pull request adding "NewLockedSource(seed inte64) Source" if people think this is reasonable.

tpu...@gmail.com

unread,
Feb 25, 2018, 3:05:32 PM2/25/18
to golang-dev
Reply all
Reply to author
Forward
0 new messages