我有一个Box<dyn Any>,我知道基本类型,所以我想在Box::downcast()(source)中优化测试.

首先,我试着用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行):

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

因为这不是我想要的优化,所以我try 了

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个arm:

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)
}

结果是(省略了zero行):

example::downcast:
        push    rax
        call    any@PLT
        pop     rcx
        ret

为什么编译器不能以这种方式优化代码?

所有组件generated by godbolt.

推荐答案

让我们尽量手动优化您的代码.如果我们手动内联downcast(),我们会得到以下结果:

pub unsafe fn downcast() -> Box<i32> {
    let value = any();
    if value.is::<i32>() {
        let raw: *mut Any = Box::into_raw(value);
        Box::from_raw(raw as *mut i32)
    } else {
        std::hint::unreachable_unchecked()
    }
}

我们可以改变这一点:

pub unsafe fn downcast() -> Box<i32> {
    let value = any();
    value.is::<i32>();
    let raw: *mut Any = Box::into_raw(value);
    Box::from_raw(raw as *mut i32)
}

value.is::<i32>()是未使用的!我们能移除它吗?问题就在这里.

is方法对dyn Any对象调用get_type_id.该方法只能在运行时确定.和it may have side effects.因此,它可以被移除.

您可以通过以下函数看到与示例中相同的冗长汇编代码:

#![feature(get_type_id)]
pub fn nop(any: Box<dyn Any>) {
    any.get_type_id();
}

现在你可能会说Any::get_type_id是由编译器统一定义的,不能被重写,但编译器还没有足够的智能来实现这一点.

Rust相关问答推荐

为什么我们不能通过指针算法将Rust原始指针指向任意地址?'

从Rust调用C++虚拟方法即使在成功执行之后也会引发Access违规错误

无法理解铁 rust &S错误处理

如何删除Mac Tauri上的停靠图标?

使用铁 rust S还原对多个数组执行顺序kronecker积

如何在递归数据 struct 中移动所有权时变异引用?

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

在rust sqlx中使用ilike和push bind

用于实现获取 struct 体 id 的特征规范

Google chrome 和 Apple M1 中的计算着色器

如何在 Rust 中将 Vec> 转换为 Vec>?

打印 `format_args!` 时borrow 时临时值丢失

rust 中不同类型的工厂函数

为什么 Rust 允许写入不可变的 RwLock?

如何使返回 XMLError 的方法与 anyhow::Error 兼容?

第 7.4 章片段中如何定义 `thread_rng`

如何在 Rust 中返回通用 struct

在 RefCell 上borrow

为什么 no_std crate 可以依赖于使用 std 的 crate?

为什么 `ref` 会导致此示例*取消引用*一个字段?