Skip to content

impl<R: RngCore> RngCore for Rc<RefCell<R>> #604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

burdges
Copy link
Contributor

@burdges burdges commented Sep 10, 2018

I'd think RefCell adjusting its borrow counter sounds more cache friendly than forking Rngs.

We cannot avoid RefCell by using UnsafeCell, etc. here because we cannot prevent user supplied methods like next_u32 from referencing us again. See the the Copy requirement on Cell::update.

I've tried to permit users to bypass the Rc counter and allocation when possible by instead providing

impl<'a,R: RngCore> RngCore for &'a ::core::cell::RefCell<R>

which should still support Rc<RefCell<R> via Rc: Deref. If you want to use this without Rc then you must remember that all methods want &mut &RefCell<R>, which may require some cajoling.

@burdges
Copy link
Contributor Author

burdges commented Sep 10, 2018

It's unclear what CI dislikes about that second commit, but it's also unclear yet if it does exactly what we want. Also verify if that &RefCell is not Send or Sync but my reading indicates that.

@vks
Copy link
Collaborator

vks commented Sep 10, 2018

The failure is on nightly only and inside packed_simd, so I think it is unrelated to your changes.

Could you please add a test exercising the newly added impl?

use std::ops::Deref;
use distributions::{Distribution, Standard};
// let rng = rng(110);
let mut r = & *::std::rc::Rc::new(::std::cell::RefCell::new(rng(110)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would not require the & * here if we had an actual impl for Rc<RefCell<R>> but navigating rustc to a &mut &RefCel<R> is tricky.

@burdges
Copy link
Contributor Author

burdges commented Sep 10, 2018

I do like this &mut &RefCell<R> trick, but.. It's simply too subtle for auto-dereferencing, so we do not actually gain an impl for Rc<RefCell<R>> this way. We do gain a technically more powerful impl, but one that requires unusual boilerplate. Do both? Thoughts?

@burdges
Copy link
Contributor Author

burdges commented Sep 10, 2018

In fact, we cannot consider this form strictly more general, even if Rc: Deref had avoided the &*, because R can only be &'a RefCell<R> not Rc<RefCell<R>> in

struct Foo<R: Rng> {
    rng: R,
    ...
}

Add back the Rc<RefCell<R>> variant?

@dhardy
Copy link
Member

dhardy commented Sep 11, 2018

If you can provide both implementations and they don't conflict, that does sound more general. Failing that, I would err on the side of implementing the form most likely to be used.

Do you have a use-case for this BTW?

@burdges
Copy link
Contributor Author

burdges commented Sep 11, 2018

I've no use case myself right now because I do protocol work in which I need reproducibility and precise control over sampling. I did not suggest doing this with Arc<Mutex<R>> because this sounds like more a numerical PRNG trick and naively atomics sounds like more the same ballpark as a cache line.

I think both forms suffice for the standard use case in which you want to pass the same &mut R into multiple closures. I suppose Rc<RefCell<R> seems more idiomatic there. And can be owned by data structures. I liked &'a RefCell<R> aesthetically for avoiding allocation, std, two unnecessary counters, drop glue, etc. ¯_(ツ)_/¯

@burdges
Copy link
Contributor Author

burdges commented Sep 11, 2018

I say keep &'a RefCell<R> for now. It's less idiomatic but many rustaceans like those optimizations, so maybe it'll catch on. :)

@dhardy
Copy link
Member

dhardy commented Sep 11, 2018

It seems on the one hand that there is reason to support both &RefCell<R> and Rc<RefCell<R>> and on the other hand like this is feature creep without much justification. Any thoughts @vks @huonw?

@burdges
Copy link
Contributor Author

burdges commented Sep 11, 2018

As an aside, an interesting research problem would be designing a non-locking reentrant numerical PRNG, albeit with poor reproducability. In essence, you'd pay slightly more computational cost than some numerical PRNGs employ some argument that no matter how the parallel executions interacted with themselves via nasty rampant undefined behavior the resulting parallel permutation still gives a sensible numerical PRNG. I expect all references into such a PRNG would require their own unique seed, which the PRNG should use to prevent operations from different threads from inverting one another. Also you'd require a bound on the number of parallel executions.

@vks
Copy link
Collaborator

vks commented Sep 11, 2018

@dhardy Personally, I would err on the side of caution and not add features without use cases.

@dhardy
Copy link
Member

dhardy commented Sep 11, 2018

@burdges so a partially-local, partially-shared PRNG? Intriguing, but as you say, more a research problem than something we should worry about solving here, and considering there are reasonably good small, fast PRNGs with 128 bits of state, I see very little motivation except as a CSPRNG (where the correctness proofs become even more important).

I think then I will close this as postponed lacking use-case; those with an interest are of course welcome to comment.

@dhardy dhardy closed this Sep 11, 2018
@dhardy dhardy added the P-postpone Waiting on something else label Sep 11, 2018
@burdges
Copy link
Contributor Author

burdges commented Sep 11, 2018

Yeah, the issue is here now so anyone who actually wants this can maybe find it and ask for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P-postpone Waiting on something else
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants