Почему компилятор Rust не оптимизирует код, предполагая, что две изменяемые ссылки не могут иметь псевдоним?
Насколько я знаю, псевдонимы ссылок и указателей могут препятствовать способности компилятора генерировать оптимизированный код, поскольку они должны обеспечивать правильное поведение сгенерированного двоичного файла в случае, когда две ссылки/указатели действительно являются псевдонимами. Например, в следующем 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?
Ответы
Ответ 1
Первоначально Rust включил атрибут LLVM noalias
, но это вызвало неправильный код. Когда все поддерживаемые версии LLVM больше не будут неправильно компилировать код, он будет снова включен.
Если вы добавите -Zmutable-noalias=yes
в опции компилятора, вы получите ожидаемую сборку:
adds:
mov eax, dword ptr [rsi]
add eax, eax
add dword ptr [rdi], eax
ret
Проще говоря, Rust повсеместно ставит эквивалент ключевого слова C restrict
everywhere, гораздо более распространенный, чем любая обычная программа на C. Это использовало angular случаи LLVM больше, чем он мог правильно обработать. Оказывается, что программисты на C и C++ просто не используют restrict
так часто, как &mut
используется в Rust.
Это происходило несколько раз.
- Руст с 1.0 до 1.7 -
noalias
включен
- Руст от 1,8 до 1,27 -
noalias
отключен
- Руст с 1.28 до 1.29 -
noalias
включен
- Руст 1.30 через??? -
noalias
отключен
Связанные с ржавчиной проблемы
Текущий случай
Предыдущий случай
Другое