How to use (insecure) anti-aliasing?

Rust has strict alias rules. But can I get around them if "I know what I'm doing"?

I am trying to convert a C function to Rust, which performs a complex operation, reading from the input buffer and writing to the destination buffer, but has a smart optimization that allows the input and output buffers to be the same:

foo(src, dst); // result is written to dst foo(buf, buf); // legal in C, does the operation in-place 

For the sake of the question, let's say something like:

 void inplace(char *src, char *dst, int len) { for(int i=0; i < len-1; i++) { dst[i] = src[i+1] * 2; // algorithm works even if src == dst } } 

In a safe subset of Rust, I must have two copies and plug-in versions of the functions fn(&mut) and fn(&, &mut) .

Is there a way to trick Rust into getting both mutable and immutable reference to the same buffer?

+5
source share
4 answers

Your main function must be implemented using unsafe code in order to use raw pointers. Source pointers allow you to bypass Rust alias rules. You can then have two functions that act as safe facades for this unsafe implementation.

 unsafe fn foo(src: *const u8, dst: *mut u8, len: usize) { for i in 0..len - 1 { *dst.offset(i as isize) = *src.offset(i as isize + 1) * 2; } } fn foo_inplace(buf: &mut [u8]) { unsafe { foo(buf.as_ptr(), buf.as_mut_ptr(), buf.len()) } } fn foo_separate(src: &[u8], dst: &mut [u8]) { assert!(src.len() == dst.len()); unsafe { foo(src.as_ptr(), dst.as_mut_ptr(), src.len()) } } fn main() { let src = &[0, 1, 2, 3, 4, 5]; let dst = &mut [0, 0, 0, 0, 0, 0]; let buf = &mut [11, 22, 33, 44, 55, 66]; foo_separate(src, dst); foo_inplace(buf); println!("src: {:?}", src); println!("dst: {:?}", dst); println!("buf: {:?}", buf); } 

as_ptr() , as_mut_ptr() and len() are as_mut_ptr() methods.

+2
source

Rust does not allow parameterization of mutability, no.

In theory, you could write some unsafe code that points to pointers to aliases, but you have to use the source pointers directly.

&mut implies that the pointer is not flattened, and the optimizer will consider it as such. Using one raw pointer and one &mut pointer can still cause problems.

+4
source

No, you cannot do it in reliable rust. You can use unsafe code to limit anti-aliasing restrictions if you want, but ...

but it has smart optimizations that allow input and output buffers to be the same

what do you call optimization, I call pessimization.

If the two buffers are guaranteed to not match, the optimizer can vectorize your code. This means that it is 4 or 8 times smaller for a loop, which greatly speeds up execution for large inputs.

However, in the absence of information about smoothing, it is pessimistic to assume that the inputs can be smoothed and, therefore, cannot do such an optimization. Even worse, not knowing how they are smoothed, he does not even know if &dst[i] == &src[i-1] or &dst[i] == &src[i] or &dst[i] == &src[i+1] ; this means that there is no prefetching, etc.


In Safe Rust, however, this information is available. This forces you to write two procedures (one for one input, one for two inputs), but both of them can be optimized accordingly.

+4
source

You can use macros to achieve this in safe code. It will work for all arguments that have a len function and support indexing. This is basically a duck seal.

 macro_rules! inplace( ($a:ident, $b:ident) => (for i in 0..($a.len()-1) { $a[i] = $b[i + 1] * 2; }) ); fn main() { let mut arr = [1, 2, 3, 4, 5]; inplace!(arr, arr); println!("{:?}", arr); } 

exits

[4, 6, 8, 10, 5]

+3
source

All Articles