-
Notifications
You must be signed in to change notification settings - Fork 432
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
Distribution::sample_iter could use owned values instead of references to be more general #602
Comments
Hello @huonw, nice to see you've taken an interest in this project again! We actually had a discussion on this a while back: #287. There's also some notes on #244. IIRC the real problem with That said, we could still switch to passing As for There are potential niggles though: does code-gen cause unnecessary copying in this case? And with non- let dist = Poisson::new(2.0);
let mut rng = ... ;
dist.sample(&mut rng); // does this copy dist?
dist.sample(&mut rng); // again?
let dist = WeightedIndex::new(..); // is Clone but not Copy
dist.sample(&mut rng); // consumes dist
// dist.sample(&mut rng); |
I've always been interested, and am so glad you've and the rest of the team have made it so much better than what it was. :) I'm now in a position to interact with open source projects more than zero, but this is definitely your project still, and please don't think you have to defer to me just because I did some work on it years ago.
I'm afraid I'm not too sure how much of that discussion applies here:
Yeah, using an inferred generic param does mean that the However, from my perspective, it is even more unfortunate to make the borrows compulsory to work around a small (hopefully temporary) language deficiency. Rust generally tends towards providing the most general APIs and then also providing glue APIs that allow them to work more nicely for specific cases (and, adds language features (like the reborrowing RFCs) once the pain points of using the general APIs in practice are understood). For instance, all the iterator adaptors take (From my perspective, the semantic split between
It could... but, I suspect for most code any unnecessary byte copies won't be noticeable, since they'll just occur during construction/movement of the Additionally, conversely, does the Finally, I suspect the common case for this API will be taking the
Yes, that's what |
Glad to hear you have more flexibility now! As far as I'm concerned, this isn't my project, but just one I've driven for a while because it wasn't getting much input and because I had a few ideas. But @pitdicker probably gave as much input as I did and several others have been involved too. I just invited you as a collaborator. |
Rust has strong no-alias rules which put it ahead of most languages. There may be other applicable optimisations though. Okay, seems to me you have a good argument here. 👍 @vks any comments? |
I think this sounds reasonable, but doc comment examples should use the
There is an orthogonal concern that cryptographic We do however sample from distributions using CSPRNGs in lattice based cryptography, anonymity tools, and byzantine consensus algorithm, ala proof-of-stake, so probably We cannot impose Also, I expect the same reasoning applied to all |
These are definitely good and work well for some cases, but in practice the benefits don't apply to every situation. In particular, I believe rustc currently only really informs LLVM of pointer-aliasing information for pointers that are function arguments (at the LLVM level), which means it doesn't work for pointers loaded from memory (including through other function arguments). For instance, in the following, // All of these are testing if the `a` load can be
// moved past the `10` write to turn the return value
// into `2 * a`
// take two pointers as arguments directly
pub fn args(x: &i32, y: &mut i32) -> i32 {
let a = *x;
*y = 10;
a + *x
}
pub struct Refs<'a> {
x: &'a i32,
y: &'a mut i32,
}
// take the pointers as a by-value struct (which
// gets exploded into individual pointer arguments
// like `args`)
pub fn struct_val(r: Refs) -> i32 {
let a = *r.x;
*r.y = 10;
a + *r.x
}
// now take the struct by reference, so the pointers
// need to be loaded from memory
pub fn struct_ref(r: &mut Refs) -> i32 {
let x = r.x;
let y = &mut *r.y;
let a = *r.x;
*r.y = 10;
a + *r.x
} example::struct_val:
mov eax, dword ptr [rdi] ; let a = *r.x;
mov dword ptr [rsi], 10 ; *r.y = 10;
add eax, eax ; a + a (optimisation worked!)
ret
example::struct_ref:
mov rcx, qword ptr [rdi] ; let x = r.x;
mov rdx, qword ptr [rdi + 8] ; let y = &mut *r.y;
mov eax, dword ptr [rcx] ; let a = *x;
mov dword ptr [rdx], 10 ; *y = 10;
add eax, dword ptr [rcx] ; a + *x (failed!)
ret
example::args:
jmp example::struct_val@PLT ; this function is identical to struct_val, they're merged |
Distribution::sample_iter
and theDistIter
type currently have signature:The lifetimes and borrowing mean that it is impossible to construct a distribution or RNG inside a function and return the
sample_iter
, e.g. if one is trying to abstract some iterator pipelineHowever, these lifetimes aren't entirely necessary, because of the impls of
Distribution
for&D
andRngCore
for&mut R
:(Side note: the
Distribution
impl could useD: ?Sized
likeRngCore
, to allow&Distribution<T>
trait objects to implDistribution<T>
.)These impls mean that the signature for
sample_iter
and definition ofDistIter
could be simplified to:The
impl
s above specifically mean that if someone wants to pass a borrowedrng
they still can, and similarly for a borrowed distribution. This also applies toRng::sample_iter
(both parameters should become owned).See https://play.rust-lang.org/?gist=c050e777ed23fb40e320ba0eb340d031&version=stable&mode=debug&edition=2015 for a playground demonstrating the problem with the old API and how the new API fixes it without breaking (much) code.
It also shows that a
by_ref
function similar toIterator::by_ref
(andRead
andWrite
) may be useful with this sort of API, to use as a postfix&
/&mut
:The text was updated successfully, but these errors were encountered: