¿Por qué el compilador Rust no optimiza el código suponiendo que dos referencias mutables no pueden tener alias?
compiler-optimization (1)
Rust originalmente
noalias
atributo
noalias
de LLVM, pero esto
causó un código mal compilado
.
Cuando todas las versiones de LLVM compatibles ya no compilan mal el código,
se volverá a habilitar
.
Si agrega
-Zmutable-noalias=yes
a las opciones del compilador, obtendrá el ensamblado esperado:
adds:
mov eax, dword ptr [rsi]
add eax, eax
add dword ptr [rdi], eax
ret
En pocas palabras, Rust puso el equivalente de la palabra clave
restrict
de C
restrict
todas partes
, mucho más frecuente que cualquier programa de C.
Esto ejerció casos de esquina de LLVM más de lo que pudo manejar correctamente.
Resulta que los programadores C y C ++ simplemente no usan
restrict
tanta frecuencia como
&mut
se usa en Rust.
Esto ha sucedido varias veces . Un historial no autorizado, basado en las etiquetas de los commits relevantes (que ignora cualquier backport que pueda haber ocurrido para las versiones de parche)
-
Rust 1.0 a 1.7 -
noalias
habilitadas -
Rust 1.8 a 1.27 -
noalias
deshabilitado -
Rust 1.28 a 1.30 -
noalias
habilitadas -
Rust 1.31 a través de ???
-
noalias
desactivadas
Problemas relacionados con el óxido
-
Caso actual
-
Caso anterior
-
Otro
- hacer uso de los metadatos de noalias con alcance de LLVM # 16515
- Optimización perdida: las referencias de los punteros no se tratan como noalias # 38941
- Noalias no es suficiente # 53105
- noalias mutables: ¿volver a habilitar permanentemente, solo para pánico = abortar o estabilizar la bandera? # 45029
Hasta donde yo sé, el alias de referencia / puntero puede dificultar la capacidad del compilador para generar código optimizado, ya que deben garantizar que el binario generado se comporte correctamente en el caso en que las dos referencias / punteros realmente alias. Por ejemplo, en el siguiente código C,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
cuando se compila con la
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
con el indicador
-O3
, emite
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
Aquí el código se almacena nuevamente en
(%rdi)
dos veces en caso de alias
int *a
e
int *b
.
Cuando explícitamente le decimos al compilador que estos dos punteros no pueden alias con la palabra clave
restrict
:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Luego, el sonido emitirá una versión más optimizada del código binario.
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Dado que Rust se asegura (excepto en un código inseguro) de que dos referencias mutables no pueden tener alias, creo que el compilador debería poder emitir la versión más optimizada del código.
Cuando
rustc 1.35.0
con el código a continuación y lo
rustc 1.35.0
con
rustc 1.35.0
con
-C opt-level=3 --emit obj
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
genera
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
Esto no aprovecha la garantía de que
b
no pueden tener alias.
¿Es esto porque el compilador Rust actual todavía está en desarrollo y aún no ha incorporado el análisis de alias para hacer la optimización?
¿Es esto porque todavía existe la posibilidad de que
b
puedan tener un alias, incluso en Rust seguro?