Насколько я знаю, псевдонимы ссылок / указателей могут препятствовать способности компилятора генерировать оптимизированный код, поскольку они должны обеспечивать правильное поведение сгенерированного двоичного файла в случае, когда две ссылки / указатели действительно являются псевдонимами. Например, в следующем коде C,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
при компиляции 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
Здесь код сохраняется в (%rdi)
два раза в случае int *a
и int *b
псевдоним.
Когда мы явно сообщаем компилятору, что эти два указателя не могут иметь псевдоним с restrict
ключевым словом:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Тогда Clang выпустит более оптимизированную версию двоичного кода:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Поскольку Rust гарантирует (за исключением небезопасного кода), что две изменяемые ссылки не могут быть псевдонимами, я думаю, что компилятор должен быть в состоянии испускать более оптимизированную версию кода.
Когда я тестирую с помощью приведенного ниже кода и компилирую его с rustc 1.35.0
помощью -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
он генерирует:
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi)
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi)
a: c3 retq
Это не воспользоваться гарантией, что a
и b
не может псевдоним.
Это потому, что текущий компилятор Rust все еще находится в разработке и еще не включил анализ псевдонимов для оптимизации?
Это потому , что есть еще шанс , что a
и b
может псевдоним, даже в безопасном Rust?
unsafe
коде псевдонимы изменяемых ссылок не допускаются и приводят к неопределенному поведению. Вы можете использовать псевдонимы-указатели, ноunsafe
код на самом деле не позволяет игнорировать стандартные правила Rust. Это просто распространенное заблуждение, на которое стоит обратить внимание.+=
операции в телеadds
могут быть интерпретированы как*a = *a + *b + *b
. Если указатели не псевдоним, они могут, вы даже можете увидеть , что составляетb* + *b
во втором ассемблерного листинга:2: 01 c0 add %eax,%eax
. Но если они делают псевдоним, они не могут, потому что к тому времени, когда вы добавляете*b
второй раз, он будет содержать значение, отличное от значения первого раза (того, которое вы храните в строке4:
первого списка asm).Ответы:
Ржавчина первоначально сделал включить LLVM в
noalias
атрибут, но этот причиненный miscompiled код . Когда все поддерживаемые версии LLVM больше не будут неправильно компилировать код, он будет снова включен .Если вы добавите
-Zmutable-noalias=yes
в параметры компилятора, вы получите ожидаемую сборку:Проще говоря, Rust повсеместно использует эквивалент
restrict
ключевого слова C , гораздо более распространенный, чем любая обычная программа на C. Это использовало угловые случаи LLVM больше, чем он мог правильно обработать. Оказывается, программисты на C и C ++ просто не используют так часто, как в Rust.restrict
&mut
Это происходило несколько раз .
noalias
включенnoalias
отключеноnoalias
включенnoalias
отключенСвязанные проблемы ржавчины
Текущий случай
Предыдущий случай
Другой
источник
restrict
и неправильно компилируют как в Clang, так и в GCC. Это не ограничивается языками, для которых недостаточно «C ++», если только вы не считаете C ++ сам в этой группе .noalias
указатели при выполнении. Он создавал новые указатели на основе входных указателей, неправильно копируяnoalias
атрибут, хотя новые указатели делали псевдоним.