Skip to content
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

Use ChaCha20 in StdRng and feature-gate SmallRng #792

Merged
merged 7 commits into from
Jun 3, 2019

Conversation

dhardy
Copy link
Member

@dhardy dhardy commented May 7, 2019

Following #789, we change the StdRng algorithm. Additionally,

  • introduce stdrng_strong and stdrng_fast feature flags
  • adjust reseeding threshold from 32 MiB to 64 kiB without observable impact on performance (in part possible because ChaCha has much faster initialisation than HC-128, and in part because the old threshold was very conservative)

Thoughts @kazcw @vks @newpavlov @burdges? I don't exactly like adding configurability here, and maybe it's not worth doing so for a 50% performance boost. ChaCha12 is used by Google's Adiantum (repo) and appears to have a good security margin, though doesn't appear to have wide usage (probably because ChaCha20 is already fast enough for most).

I do think we should only use generators with a good level of trust, thus have not added ChaCha8 as an option (even though it remains unbroken).

Note: the names stdrng_conservative and stdrng_good may be more appropriate, but are likely also confusing.

benches/generators.rs Outdated Show resolved Hide resolved
@dhardy dhardy changed the title Features StdRnd and configuration features May 7, 2019
@dhardy
Copy link
Member Author

dhardy commented May 7, 2019

Note: I need to fix the tests, which shouldn't be using StdRng (how many times have we said don't use it when needing reproducibility?)

@burdges
Copy link
Contributor

burdges commented May 7, 2019

Aren't features additive? I'm dubious about this plan, but if doing it then using only one feature sounds best. I'd think stdrng_conservative could be default set, maybe meaning ChaCha20, but if anyone wants faster then they should drop the default feature, maybe meaning ChaCha12.

@vks
Copy link
Collaborator

vks commented May 7, 2019

Maybe it's better to be more conservative and provide no option between ChaCha12 and ChaCha20? We can suggest in the documentation to use ChaCha12 directly via rand_chacha if the additional performance is required.

@newpavlov
Copy link
Member

newpavlov commented May 7, 2019

I am not sure if I like this approach, to me it looks like features misuse. Also shouldn't be newtype pattern used here instead of type alias? For example it's possible to write code like this:

fn foo(rng: &mut StdRng) { .. }

foo(&mut ChaCha12Rng::from_entropy());

So changing StdRng underlying algorithm can be technically viewed as a breaking change. And of course turning feature may break build as well.

@vks
Copy link
Collaborator

vks commented May 7, 2019

Also shouldn't be newtype pattern used here instead of type alias?

I believe this is the case for StdRng. However, Core is just a type alias, but I think it is not exported

@dhardy
Copy link
Member Author

dhardy commented May 7, 2019

@burdges yes, but disabling a default feature which is used from multiple sub-crates is hard. @vks the point is to allow tuning of ThreadRng.

@newpavlov I agree, this is feature mis-use. It would be much better if Cargo allowed --features stdrng=ChaCha20 for example, and even better if this could only be specified by the final binary. However, we can't do that. If such a feature became available I would prefer to use that, so of course it is better not to include a feature now we expect to break now — except this is interesting in that it provides a way for users heavily reliant on ThreadRng to tune their apps.

We do use the newtype pattern? The aliases are strictly for internal use.

Of course, ThreadRng performance is probably not a bottleneck for most apps since in that case it would make more sense to use another RNG directly, so quite possibly adding tuning for it is pointless.

@newpavlov
Copy link
Member

We do use the newtype pattern? The aliases are strictly for internal use.

Ah, yes, you are right.

@burdges
Copy link
Contributor

burdges commented May 7, 2019

I've no idea what StdRng gets used for. It's not used by ThreadRng, which sounds like the unique place.

@dhardy
Copy link
Member Author

dhardy commented May 8, 2019

I've no idea what StdRng gets used for. It's not used by ThreadRng, which sounds like the unique place.

Me neither actually. By the time you've filtered out the duplicates and re-definitions... not so many. In contrast SmallRng has less total hits, but more actual uses come up (maybe because StdRng has been a part of Rand for much longer).

So I guess we drop the configuration idea. Probably we should keep StdRng because it's basically free (we need the dependency for ThreadRng already).

Shall we feature-gate SmallRng behind features=small? That drops a dependency in standard builds while keeping it (seems useful enough overall, and there isn't another good home).

@vks
Copy link
Collaborator

vks commented May 9, 2019

Shall we feature-gate SmallRng behind features=small? That drops a dependency in standard builds while keeping it (seems useful enough overall, and there isn't another good home).

I think this is a good idea. It will presumably also make it easier to gauge how much SmallRng is used.

@dhardy dhardy changed the title StdRnd and configuration features Use ChaCha20 in StdRng and feature-gate SmallRng May 13, 2019
@dhardy
Copy link
Member Author

dhardy commented May 13, 2019

Rebased. First and last commits are the same, the middle two are new. I think this covers all suggestions.

src/rngs/small.rs Show resolved Hide resolved
@dhardy dhardy mentioned this pull request May 15, 2019
22 tasks
@dhardy
Copy link
Member Author

dhardy commented May 15, 2019

The Miri test is failing:

error[E0080]: Miri evaluation error: miri does not support inline assembly
  --> /home/travis/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/../stdsimd/crates/core_arch/src/x86/cpuid.rs:68:9
[snip]
   = note: inside call to `std::detect::check_for` at <::std::std_detect::detect::arch::is_x86_feature_detected macros>:30:37
   = note: inside call to `c2_chacha::guts::init_chacha::dispatch_init` at <::ppv_lite86::x86_64::dispatch_light128 macros>:12:28
[snip]
note: inside call to `<rand_chacha::ChaCha8Rng as rand_core::SeedableRng>::seed_from_u64` at src/lib.rs:566:9
  --> src/lib.rs:566:9
   |
566|         rand_chacha::ChaCha8Rng::seed_from_u64(seed)

So I guess for Miri's sake, we should use a simpler RNG in our tests (perhaps PCG like in rand_distr, if somehow Cargo will allow a dependency to be both optional and a dev-dependency).

@dhardy
Copy link
Member Author

dhardy commented May 15, 2019

The WASM/Emscripten test fails with a linker error. It is not very decipherable, but running the following (prerequisites are in the .travis config, 194-199) on rand_chacha causes a similar error:

~/projects/rand/rand/rand_chacha$ EMCC_CFLAGS="-s ERROR_ON_UNDEFINED_SYMBOLS=0" cargo web test --target wasm32-unknown-emscripten --no-run
info: downloading component 'rust-std' for 'wasm32-unknown-emscripten'
info: installing component 'rust-std' for 'wasm32-unknown-emscripten'
   Compiling lazy_static v1.3.0
   Compiling ppv-lite86 v0.2.3
   Compiling rand_core v0.4.0 (/home/dhardy/projects/rand/rand/rand_core)
   Compiling rand_chacha v0.2.1 (/home/dhardy/projects/rand/rand/rand_chacha)
   Compiling c2-chacha v0.2.2
error: linking with `emcc` failed: exit code: 1

@kazcw is there some way we can make rand_chacha compatible with Emscripten (if necessary, using a slower fallback)?

If this also allows us to work around the Miri issue (#[cfg(miri)]) then even better.

Note: of course we could just cfg the whole thing and include the old implementation, but hopefully there's a solution with less code redundancy. If need be Miri can use a different RNG so don't design around that requirement.

// Timer check is already quite permissive of failures so we don't
// expect false-positive failures, i.e. any error is irrecoverable.
#[cfg(feature = "std")] {
Error::with_cause(ErrorKind::Unavailable, "timer jitter failed basic quality tests", err)
Error::with_cause(ErrorKind::Unavailable, "timer jitter failed basic quality tests", _err)
}
#[cfg(not(feature = "std"))] {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe it's better to add let _ = err; here instead of renaming err to _err?

Copy link
Member Author

Choose a reason for hiding this comment

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

Does it matter?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer it, because I think it reflects intent better, but ultimately it's not important and I'm fine with either way.

@vks vks added this to the 0.7 release milestone May 15, 2019
@kazcw
Copy link
Contributor

kazcw commented May 16, 2019

@dhardy I don't know off the top of my head what problem emscripten would have with the portable fallback; it all safe code and works on x86/arm/mips. I'll dig into it probably this weekend.

@vks
Copy link
Collaborator

vks commented May 23, 2019

If I understood correctly, the plan for this PR is the following:

  • Use a copy-and-pasted PCG generator for value-stability tests, instead of ChaCha8.
  • Use rand_hc instead of rand_chacha for Emscripten targets.

@dhardy
Copy link
Member Author

dhardy commented May 23, 2019

Done. The result is a little hacky, but not too bad.

It seems there is no problem with depending on the same thing twice within Cargo.toml.

Copy link
Collaborator

@vks vks left a comment

Choose a reason for hiding this comment

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

rand_distr/tests/uniformity.rs probably should be updated to not use SmallRng. Otherwise this looks very good!

@dhardy
Copy link
Member Author

dhardy commented May 23, 2019

Right. Switched to Pcg32 since we depend on that crate anyway, but could also use thread_rng().

@vks
Copy link
Collaborator

vks commented May 31, 2019

There are the following failures:

@dhardy
Copy link
Member Author

dhardy commented May 31, 2019

My opinion here is that the Miri and WASM failures lie outside the scope of the Rand project, and unless we get help addressing these issues, we should not block the 0.7 release on switching StdRng to ChaCha (it was never originally a target anyway).

@kazcw
Copy link
Contributor

kazcw commented May 31, 2019

I released ppv-lite86 0.2.4, so Miri should pass now.

@dhardy
Copy link
Member Author

dhardy commented Jun 1, 2019

Thanks @kazcw, you've been very helpful on this.

The WASM failure is about a u128 rotation, but this is not emscripten. Perhaps a bug? I can't reproduce with a wrapper function.

@kazcw
Copy link
Contributor

kazcw commented Jun 1, 2019

Wow, that's interesting. I created a project that just has a function that invokes u128::rotate_right:

If I use cargo build --target wasm32-unknown-unknown, I get a .wasm that can be imported without error.

But if I build it with cargo web build --target=wasm32-unknown-unknown, I get a module that can't be loaded:
In headless Chrome: CompileError: AsyncCompilation: (null): Compiling wasm function "_ZN4core3num22_$LT$impl$u20$u128$GT$12rotate_right17hf9a8f167bd77e313E" failed: call[3] expected type i32, found get_local of type i64 @+2163
In Firefox: Error loading Rust wasm module 'test_rotate': CompileError: "wasm validation error: at offset 1206: type mismatch: expression has type i64 but expected i32"

rotate_left is also broken, but other u128 ops that I tried work.

I don't see how this could be anything but a rustc bug, but why does it only break when built with cargo-web?

Filed a bug in cargo-web: koute/cargo-web#193

@kazcw
Copy link
Contributor

kazcw commented Jun 1, 2019

Ok, as of ppv-lite86 0.2.5 I don't rotate any u128s.

@vks
Copy link
Collaborator

vks commented Jun 3, 2019

I think this can be merged after we

Then all tests should pass.

… test

We want a value-stable RNG to avoid future vector changes
which means using something outside of Rand. Pcg32 is a good
fit and is easy to embed if that is ever necessary.
seq::test::test_slice_choose updated with better bounds
This is considered an ugly hack, but easier than making the new ChaChaRng
compatible with Emscripten. Potentially support for Emscripten will be dropped
in the future, if it doesn't get support for u128.
@dhardy
Copy link
Member Author

dhardy commented Jun 3, 2019

Rebased. Reordered commits and tidied up (squashed) some. Adjusted the test_slice_choose test with better bounds which failed with the new RNG.

@dhardy
Copy link
Member Author

dhardy commented Jun 3, 2019

require ppv-lite86 >= 0.2.5

Where do you propose we do this? We don't have a direct dependency. I don't think it's worth bothering with minimum patch version requirements personally, especially where this only affects minor platforms (WASM in this case).

@vks
Copy link
Collaborator

vks commented Jun 3, 2019

Where do you propose we do this? We don't have a direct dependency. I don't think it's worth bothering with minimum patch version requirements personally, especially where this only affects minor platforms (WASM in this case).

Fair enough, this should be fine then.

@vks
Copy link
Collaborator

vks commented Jun 3, 2019

The tests passed! 🎉

@dhardy dhardy merged commit 245fde0 into rust-random:master Jun 3, 2019
@dhardy dhardy mentioned this pull request Jun 4, 2019
@dhardy dhardy deleted the features branch June 4, 2019 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants