我有一个 Box<dyn Any>
,并且我知道其中的基础类型,因此我想优化掉Box::downcast()
中的测试(源代码)。
首先我尝试使用 std::hint::unreachable_unchecked()
:
pub unsafe fn downcast() -> Box<i32> {
let value = any();
if let Ok(value) = value.downcast() {
value
} else {
std::hint::unreachable_unchecked()
}
}
和
pub unsafe fn downcast() -> Box<i32> {
any().downcast().map_err(|_| std::hint::unreachable_unchecked()).unwrap()
}
rustc -C opt-level=3
使用,两者结果均为以下内容(省略了40行):
Using rustc -C opt-level=3
, both results are the following (with 40 lines omitted):
example::downcast:
push rbx
sub rsp, 16
call any@PLT
mov rbx, rax
mov qword ptr [rsp], rax
mov qword ptr [rsp + 8], rdx
mov rdi, rax
call qword ptr [rdx + 24]
mov rax, rbx
add rsp, 16
pop rbx
ret
mov rbx, rax
mov rdi, rsp
call core::ptr::drop_in_place
mov rdi, rbx
call _Unwind_Resume@PLT
ud2
由于这不是我寻找的优化方式,因此我尝试了
pub unsafe fn downcast() -> Box<i32> {
let value = any();
std::intrinsics::assume(value.is::<i32>());
value.downcast().unwrap()
}
但情况变得更糟了(省略了118行):
example::downcast:
push r15
push r14
push rbx
sub rsp, 32
call any@PLT
mov rbx, rax
mov r14, rdx
mov qword ptr [rsp], rax
mov qword ptr [rsp + 8], rdx
mov r15, qword ptr [rdx + 24]
mov rdi, rax
call r15
mov qword ptr [rsp + 16], rbx
mov qword ptr [rsp + 24], r14
mov rdi, rbx
call r15
movabs rcx, -5015437470765251660 ;TypeId::of::<i32>()
cmp rax, rcx
jne .LBB5_7
mov rax, rbx
add rsp, 32
pop rbx
pop r14
pop r15
ret
.LBB5_7:
mov rdi, rbx
mov rsi, r14
call core::result::unwrap_failed
ud2
mov rbx, rax
lea rdi, [rsp + 16]
call core::ptr::drop_in_place
mov rdi, rbx
call _Unwind_Resume@PLT
ud2
mov rbx, rax
mov rdi, rsp
call core::ptr::drop_in_place
mov rdi, rbx
call _Unwind_Resume@PLT
ud2
我希望生成像这样的代码,这是从Box::downcast
中的Ok
分支:
pub unsafe fn downcast() -> Box<i32> {
let value = any();
let raw: *mut dyn Any = Box::into_raw(value);
Box::from_raw(raw as *mut i32)
}
这将导致如下结果(省略零行):
example::downcast:
push rax
call any@PLT
pop rcx
ret
为什么编译器不能以这样的方式优化代码?
Box :: downcast
的Ok
分支:”-- 在Ok
分支之前,您正在省略一个调用:self.is::<T>()
。如果在那里插入该调用,即使您丢弃结果(因此let value = any(); value.is::<i32>(); let raw: ...
),也将获得相同的代码。我还不知道它实现中具体难以优化的原因是什么。 - user743382self.is::<T>()
内部保留了对any::get_type_id()
的虚拟调用。编译器不能看穿它,并在其中加入一个取消块,以防它具有副作用。 - wartmanm