据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实是别名的情况下,生成的二进制文件的行为正确。例如,在以下C代码中,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
Run Code Online (Sandbox Code Playgroud)
当clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
用-O3
标志编译时,它发出
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi) # The first time
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi) # The second time
a: c3 retq
Run Code Online (Sandbox Code Playgroud)
下面的代码存回(%rdi)
两次的情况下,int *a
和int *b
别名。
当我们明确告诉编译器这两个指针不能使用restrict
关键字别名时:
void adds(int * restrict a, int * restrict …
Run Code Online (Sandbox Code Playgroud) C++11 引入了一种新的内存模型,让“运行”C++11 代码的抽象机器有关于多线程的概念。它还引入了一组内存顺序,内存加载/存储操作遵循这些顺序。
C++20 的维基百科页面说它有
修改后的内存模型。
它给出的参考资料说 C++11 的内存模型有许多缺陷,C++20 将对其进行修改。
有人可以举例说明 C++11 的内存模型带来的问题,以及 C++20 中的问题如何解决吗?
相关问题: C++11的内存模型介绍
我认为一旦一个对象被移动,它在堆栈上占用的内存可以被重用于其他目的。但是,下面的最小示例显示了相反的情况。
#[inline(never)]
fn consume_string(s: String) {
drop(s);
}
fn main() {
println!(
"String occupies {} bytes on the stack.",
std::mem::size_of::<String>()
);
let s = String::from("hello");
println!("s at {:p}", &s);
consume_string(s);
let r = String::from("world");
println!("r at {:p}", &r);
consume_string(r);
}
Run Code Online (Sandbox Code Playgroud)
使用--release
标志编译代码后,它在我的计算机上提供以下输出。
String occupies 24 bytes on the stack.
s at 0x7ffee3b011b0
r at 0x7ffee3b011c8
Run Code Online (Sandbox Code Playgroud)
很明显,即使s
被移动,r
也不会重用最初属于 的堆栈上的 24 字节块s
。我认为重用移动对象的堆栈内存是安全的,但为什么 Rust 编译器不这样做呢?我错过了任何角落案例吗?
更新:如果我s
用大括号括起来,r
可以重用堆栈上的 24 字节块。
String occupies 24 bytes on …
Run Code Online (Sandbox Code Playgroud) 下面的示例代码有些精心设计,但说明了我的主要关注点。代码编译完美。
struct SliceWrapper<'a>(&'a mut[i32]);
impl<'a> SliceWrapper<'a> {
fn clear(&mut self) {
self.0 = &mut [];
}
}
fn main() {
let slice = &mut [1, 2, 3];
let mut wrapper = SliceWrapper(slice);
wrapper.clear();
}
Run Code Online (Sandbox Code Playgroud)
该行self.0 = &mut [];
有效,但如果我们查看它们的生命周期,则非常奇怪:对局部变量的引用被分配给self.0
,它存在于方法调用之外clear()
。
更令人困惑的是,如果我将该行更改为self.0 = &mut [0];
,那么编译器会向我抛出一个错误:创建一个临时文件,该临时文件在仍在使用时被释放。
所以我猜 Rust 编译器对待生命周期的方式&mut []
不同。真的吗?什么是精确的生命周期规则&mut []
?
在下面的一段代码中,我试图了解通用生命周期参数'a
是如何特化的。
struct Wrapper<'a>(&'a i32);
fn foo() {
let mut r;
{
let x = 0; // the lifetime of x, call it 'x, starts from here |
r = Wrapper(&x); // 'a parameter in Wrapper is specialized to 'x |
drop(r); // |
} // --------------------------------- 'x ends here |
{
let y = 0; // the lifetime of y, call it 'y, starts from here |
// 'y is distinct from 'x |
// neither outlives the …
Run Code Online (Sandbox Code Playgroud) 我读过什么是非词法生命周期?. 使用非词法借用检查器,编译以下代码:
fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0]; // borrows `scores`, but never used
// its lifetime can end here
scores.push(4); // borrows `scores` mutably, and succeeds
}
Run Code Online (Sandbox Code Playgroud)
在上面的情况下似乎是合理的,但是当涉及到互斥锁时,我们不希望它过早释放。
在下面的代码中,我想先锁定一个共享结构,然后执行一个闭包,主要是为了避免死锁。但是,我不确定锁是否会过早释放。
use lazy_static::lazy_static; // 1.3.0
use std::sync::Mutex;
struct Something;
lazy_static! {
static ref SHARED: Mutex<Something> = Mutex::new(Something);
}
pub fn lock_and_execute(f: Box<Fn()>) {
let _locked = SHARED.lock(); // `_locked` is never used.
// does its lifetime end here?
f();
}
Run Code Online (Sandbox Code Playgroud)
Rust …
Rust 在调试和发布模式下以不同的方式处理有符号整数溢出。当它发生时,Rust 在调试模式下会发生恐慌,而在发布模式下默默地执行二进制补码包装。
据我所知,C/C++ 将有符号整数溢出视为未定义行为,部分原因是:
因此,如果 Rust 编译器确实在有符号整数方面执行与 C/C++ 编译器相同类型的优化,那么为什么The Rustonomicon指出:
无论如何,Safe Rust 不会导致未定义行为。
或者即使 Rust 编译器不执行这样的优化,Rust 程序员仍然不希望看到有符号整数环绕。不能称为“未定义行为”吗?
在不安全代码指南参考中,它说
Rust 中的所有内部突变都必须发生在 内部
UnsafeCell
,因此所有具有内部可变性的数据结构都必须(直接或间接)用于UnsafeCell
此目的。
另外,在关于 的讨论UnsafeCell
中,它说
UnsafeCell
基本上是编译器的优化障碍。
确实如此UnsafeCell
是 Rust 中的编译器优化障碍吗?如果是,标准库源代码中的哪一行发出了屏障,它是如何工作的?
[更新]
相关问题的答案给出了非常好的解释。TL;DR 版本是:UnsafeCell<T>
被标记为#[lang = "unsafe_cell"]
强制它在以下情况下保持不变T
。
现在我认为这与优化没有太大关系,但与寿命分析的相互作用更密切。
对于 Rust 中的方差概念,The Rustonomicon Book给出了详细的解释。
我正在调查阻止 Rust 编译器优化某些代码片段的可能原因。我在 rust-lang 的一个 issue 中发现了这条评论,它提醒了我。
我们不能优化可变借用的局部变量的存储,因为正如@matthewjasper 在 #61430 中指出的那样,并没有决定以下是 UB:
Run Code Online (Sandbox Code Playgroud)let mut x = String::new(); let p = &mut x as *mut String; let y = x; p.write(String::new());
我认为x
当它移动到y
. p
在.write()
通过时晃来晃去。但为什么这不决定为UB?
我想决定一个字符串是否以Rust中的另一个字符串开头。
我看到了类似的问题,要求在Rust中将String与文字字符串匹配,但这在这里不起作用。例如,在以下代码中,
fn main() {
let a = String::from("hello world!");
let b = String::from("hello");
a.starts_with(b);
}
Run Code Online (Sandbox Code Playgroud)
编译器抱怨:
fn main() {
let a = String::from("hello world!");
let b = String::from("hello");
a.starts_with(b);
}
Run Code Online (Sandbox Code Playgroud)
我可以手动实现简单的功能,但这就是重新实现轮子。如何在Rust中很好地完成此工作?