< 返回版块

lithbitren 发表于 2023-01-29 22:35

Tags:if-else,match,表驱动

在流程控制上,rust历来都比较推荐match,功能上十分完备,可以很大程度上替代传统语言的if-else语句,也能很大程度替代其他语言在表驱动的写法。

说是这么说,但实际性能是怎样的却很少有人说明,只看汇编码的话勉强可以看出match语句比if-else语句少掉很多比较,但实际性能怎样还得测一测。

测试的方法则是一个通过流程控制实现的循环计数功能,分别测试纯粹的match匹配,花式match-if匹配,传统if-else流程控制,以及通过HashMap<usize, Box<dyn Fn(&mut usize)>来实现表驱动。

其中表驱动这种写法很多语言是用switch实现的,rust一般是用match实现的,但也有部分语言方法是hashmap实现的,rust讲实话很少实现以函数为值的容器,其中涉及的所有权问题比较复杂,虽然是函数但还得包动态类型智能指针,不像某些语言直接把函数放进值里就可以了。这次测试也只是用了闭包Fn实现,本来想写更加不卫生的无参数FnMut,但怎么都编译不通过,只能凑合测测看了。

具体在实现的代码如下,假设其为64个分支:

fn match_test(n: usize, mut x: usize) -> usize {
    for _ in 0..n {
        match x {
            1 => x = 2,
            2 => x = 3,
            ...
            63 => x = 64,
            _ => x = 1
        }
    }
    x
}

fn match_if_test(n: usize, mut x: usize) -> usize {
    for _ in 0..n {
        match x {
            y if y == 1 => x = 2,
            y if y == 2 => x = 3,
            ...
            y if y == 63 => x = 64,
            _ => x = 1
        }
    }
    x
}

fn if_else_test(n: usize, mut x: usize) -> usize {
    for _ in 0..n {
        if x == 1 { x = 2 }
        else if x == 2 { x = 3 }
        ...
        else if x == 63 { x = 64 }
        else { x = 1 }
    }
    x
}

fn table_driven_hashmap_test(n: usize, mut x: usize) -> usize {
    use std::collections::HashMap;
    // 本来想写更加不卫生的无参数FnMut,但最终也没把x的所有权问题解决
    type Callback = Box<dyn Fn(&mut usize)>;
    let m: HashMap<usize, Callback> = HashMap::from([
        (1, Box::new(|x: &mut usize| *x = 2) as Callback),
        (2, Box::new(|x: &mut usize| *x = 3) as Callback),
        ...
        (63, Box::new(|x: &mut usize| *x = 64) as Callback),
    ]);
    let default = |x: &mut usize| *x = 1;
    for _ in 0..n {
        if m.get(&x).map(|func| func(&mut x)).is_none() {
            // 当 x 不等于 1 到 63 的时候,x = 1;
            default(&mut x);
        }
    }
    x
}

fn table_driven_vec_test(n: usize, mut x: usize) -> usize {
    type Callback<'a> = &'a dyn Fn(&mut usize);
    let v: &[Callback<'_>] = &[
        &(|x: &mut usize| *x = 1) as Callback,
        &(|x: &mut usize| *x = 2) as Callback,
        &(|x: &mut usize| *x = 3) as Callback,
        ...
        &(|x: &mut usize| *x = 64) as Callback,
    ];
    let default = |x: &mut usize| *x = 1;
    for _ in 0..n {
        if v.get(x).map(|func| func(&mut x)).is_none() {
            default(&mut x);
        }
    }
    x
}

通过宏重写了复用的逻辑,分别测试不同的分枝数的时间,循环次数取了n = 10_000_000,最后得到如下测试结果:

branches: 4
match: 3.3904ms, 3.6673ms, 4.7517ms, 3.3143ms, 3.3142ms, 3.4453ms, 3.612ms, 3.3194ms, 3.3141ms, 3.315ms, 
match_if: 3.7305ms, 4.1816ms, 3.9521ms, 3.6463ms, 3.6331ms, 4.2191ms, 4.0577ms, 3.6995ms, 3.7195ms, 4.1883ms, 
if_else: 5.4293ms, 3.8502ms, 4.3737ms, 3.9505ms, 3.7267ms, 3.4774ms, 4.1949ms, 3.9994ms, 3.7674ms, 3.6ms, 
table_driven_hashmap: 103.9701ms, 122.3524ms, 122.7845ms, 100.2528ms, 106.5108ms, 127.5172ms, 105.7942ms, 129.1624ms, 165.0806ms, 115.4098ms, 
table_driven_vec: 16.1917ms, 14.0669ms, 14.0214ms, 14.0425ms, 13.8328ms, 19.6705ms, 21.2281ms, 13.5288ms, 22.032ms, 17.1706ms, 
branches: 8
match: 3.6455ms, 3.8793ms, 3.5487ms, 3.7148ms, 3.7006ms, 3.6769ms, 3.6659ms, 3.7404ms, 3.6811ms, 3.6833ms, 
match_if: 3.9426ms, 4.4188ms, 3.8393ms, 4.4766ms, 3.8199ms, 3.9531ms, 3.7941ms, 3.8904ms, 3.9169ms, 3.9105ms, 
if_else: 4.0554ms, 3.9229ms, 3.7935ms, 4.0178ms, 4.281ms, 3.9466ms, 3.8345ms, 4.0638ms, 4.6341ms, 3.8491ms, 
table_driven_hashmap: 113.2449ms, 127.943ms, 128.6095ms, 112.7296ms, 125.1844ms, 133.5039ms, 154.9556ms, 224.6223ms, 191.0714ms, 104.3797ms, 
table_driven_vec: 18.8915ms, 16.06ms, 16.6736ms, 15.141ms, 16.5778ms, 15.9272ms, 15.4643ms, 15.5129ms, 15.4069ms, 15.4384ms, 
branches: 16
match: 3.82ms, 3.9299ms, 3.6091ms, 4.0194ms, 3.8655ms, 3.5625ms, 3.5489ms, 3.5875ms, 3.6575ms, 3.6409ms, 
match_if: 3.7758ms, 3.5685ms, 3.6397ms, 3.7803ms, 3.5683ms, 3.5808ms, 3.6137ms, 4.3534ms, 4.121ms, 3.6389ms, 
if_else: 3.6131ms, 4.0447ms, 3.9562ms, 3.5895ms, 3.5671ms, 3.8686ms, 3.6878ms, 3.5646ms, 3.5976ms, 3.6763ms, 
table_driven_hashmap: 116.7476ms, 120.816ms, 113.8963ms, 121.6966ms, 102.2712ms, 124.4602ms, 116.9638ms, 128.0126ms, 137.3728ms, 142.8418ms, 
table_driven_vec: 19.2862ms, 15.2574ms, 15.4181ms, 14.7007ms, 14.89ms, 28.9602ms, 55.532ms, 28.3054ms, 26.2988ms, 67.9233ms, 
branches: 32
match: 3.5513ms, 3.732ms, 3.6125ms, 3.4187ms, 3.3474ms, 3.3586ms, 3.3585ms, 3.4791ms, 4.2535ms, 3.3596ms, 
match_if: 3.5743ms, 3.9367ms, 3.6296ms, 3.339ms, 3.3292ms, 3.4128ms, 3.4474ms, 3.5822ms, 3.4925ms, 3.433ms, 
if_else: 4.21ms, 3.6252ms, 3.4172ms, 3.4126ms, 3.4433ms, 3.6506ms, 3.4267ms, 3.402ms, 3.4292ms, 3.4252ms, 
table_driven_hashmap: 135.3664ms, 103.676ms, 113.1637ms, 120.5619ms, 110.7571ms, 105.7341ms, 119.5289ms, 132.2703ms, 108.6024ms, 117.8609ms, 
table_driven_vec: 16.8557ms, 16.1205ms, 15.5395ms, 15.3842ms, 15.4809ms, 15.6577ms, 15.2929ms, 15.3133ms, 15.6808ms, 15.3334ms, 
branches: 64
match: 3.8765ms, 4.403ms, 3.2189ms, 3.2614ms, 5.4701ms, 3.6202ms, 3.2225ms, 3.2231ms, 3.5133ms, 3.8491ms, 
match_if: 3.6157ms, 3.8887ms, 3.6092ms, 4.6589ms, 4.1004ms, 3.46ms, 4.167ms, 3.5751ms, 3.3842ms, 3.3782ms, 
if_else: 3.8066ms, 5.2757ms, 4.8017ms, 3.409ms, 3.4739ms, 3.4244ms, 3.509ms, 3.3838ms, 3.4959ms, 3.3824ms, 
table_driven_hashmap: 198.0397ms, 155.8294ms, 138.0204ms, 162.6539ms, 149.2959ms, 153.7422ms, 152.5761ms, 131.911ms, 138.5925ms, 153.5351ms, 
table_driven_vec: 16.9102ms, 16.3827ms, 15.8785ms, 16.5236ms, 15.6935ms, 15.5552ms, 15.6869ms, 15.5644ms, 15.4713ms, 15.5346ms, 
branches: 128
match: 3.9663ms, 4.8639ms, 3.7769ms, 3.6452ms, 3.6132ms, 4.4149ms, 3.6532ms, 3.7608ms, 3.6438ms, 3.8445ms, 
match_if: 6.4039ms, 6.2458ms, 6.296ms, 6.2953ms, 6.2581ms, 6.4576ms, 6.3213ms, 6.3387ms, 6.3825ms, 6.2506ms, 
if_else: 7.1545ms, 6.5482ms, 6.7545ms, 6.6666ms, 6.6354ms, 6.8014ms, 6.6641ms, 6.7175ms, 6.7168ms, 7.4704ms, 
table_driven_hashmap: 182.5284ms, 183.1441ms, 181.4559ms, 183.8605ms, 184.5005ms, 180.1116ms, 193.7634ms, 188.7075ms, 185.761ms, 196.2352ms, 
table_driven_vec: 33.681ms, 30.236ms, 29.9209ms, 50.8282ms, 60.2091ms, 53.2975ms, 28.2092ms, 30.7684ms, 61.158ms, 31.9083ms, 
branches: 1024
match: 3.9626ms, 3.9671ms, 3.9628ms, 4.4519ms, 3.9806ms, 3.9601ms, 4.1644ms, 4.4445ms, 3.9706ms, 3.9609ms, 
match_if: 15.5266ms, 14.4545ms, 14.6208ms, 15.4482ms, 14.4482ms, 14.5209ms, 14.5363ms, 14.6093ms, 19.3402ms, 18.0519ms, 
if_else: 16.0209ms, 15.0817ms, 14.5871ms, 14.6568ms, 15.6924ms, 14.4908ms, 15.4812ms, 14.8693ms, 15.5043ms, 15.7196ms, 
table_driven_hashmap: 249.0023ms, 222.2315ms, 222.863ms, 232.9312ms, 253.3306ms, 246.7888ms, 239.6788ms, 242.6305ms, 228.6975ms, 229.5875ms, 
table_driven_vec: 87.5138ms, 91.1127ms, 97.3113ms, 104.7676ms, 85.8476ms, 85.7071ms, 100.7908ms, 109.6692ms, 91.2772ms, 90.6162ms, 

可以看出,在分支比较少的情况下,即64个分支以内的情况下,通过编译器的优化,match/match-if/if-else在性能上没啥区别,而表驱动走的数值哈希,在不扩容的情况下理论上性能也不会有太大变化,但都比前三者差上15-20倍。

在分支变大到1024的时候,可以明显看出match的性能优势,可以说几乎没有减弱,而match-if和if-else都同时出现了性能下降,比小分支时候的性能差了3-5倍,至于hashmap的表驱动,性能也再下降了一倍。

再往上翻倍分支我这就是栈溢出,所以最多就测到了1024。

当然,在绝大多数情况下,我们都用不到这么多分支,我在其他语言遇到过最大if-else屎山大约也就中几十个分支,就算写了注释也看不懂,最后选择在屎山上再上一坨。

对于rust项目来说,以后遇到可能出现屎山的情况,如果是为了匹配清晰还是优先选match比较好,就像各种教程推荐的那样优先用match。至于hashmap那种表驱动写法,又难写,性能又不行,除非有必须使用的场景(可能是某些路由匹配),在日常分支判断里实在是不太推荐(指的是在某些语言里用表驱动实现分支)。不过话说回来,hashmap更动态,更适合基础设施和框架,可以根据配置文件来动态插入,不像match和if-else只能修改代码写死,使用场景还是略有区别。

加测了数组的表驱动,数组取下表性能远快于hashmap取键,不过还是比match-if/if-else慢上4-6倍,而且随着数组变大性能也会下降(尽管测试方法已经几乎算是顺序读取数据了)。

评论区

写评论
作者 lithbitren 2023-02-07 14:47

确实,这只是编译优化前的代码展开,和最终优化结果不能划等号,但看到表驱动展开后的这堆代码,想优化到match或ifelse级别的性能实在有点玄幻。当然编译器优化本身就很玄幻,做性能测试的时候非常害怕一不小就把整段代码优化没了。

--
👇
苦瓜小仔: 很棒!不过我感觉性能分析看 MIR 有点不对,因为 MIR 是给 LLVM IR 的,它适合分析 Rust 相关的、优化前东西(比如语法脱糖、借用检查、控制流图),不太适合分析性能这种依赖于优化后的东西。

苦瓜小仔 2023-02-07 09:44

很棒!不过我感觉性能分析看 MIR 有点不对,因为 MIR 是给 LLVM IR 的,它适合分析 Rust 相关的、优化前东西(比如语法脱糖、借用检查、控制流图),不太适合分析性能这种依赖于优化后的东西。

作者 lithbitren 2023-02-07 04:28

看了MIR,表驱动一来是闭包函数元素存在堆上,调用比match的栈上调用慢,再者就是闭包函数没有inline内联优化,整个函数存在调用过程,函数调用过程本身就比普通作用域调用要复杂得多,还得传入引用进行引用修改,还有就是闭包本身不管是用box指针还是生命周期标记,用了dyn的话本身引用调用也有运行时开销,所以还是能match就match吧。

下面是去掉循环后且分支缩减为3个的MIR展开:

pub fn match_test(mut x: usize) -> usize {
    match x {
        1 => x = 2,
        2 => x = 3,
        _ => x = 1
    }
    x
}

pub fn match_if_test(mut x: usize) -> usize {
    match x {
        y if y == 1 => x = 2,
        y if y == 2 => x = 3,
        _ => x = 1
    }
    x
}

pub fn if_else_test(mut x: usize) -> usize {
    if x == 1 { x = 2 }
    else if x == 2 { x = 3 }
    else { x = 1 }
    x
}

pub fn table_driven_hashmap_test(mut x: usize) -> usize {
    use std::collections::HashMap;
    type Callback = Box<dyn Fn(&mut usize)>;
    let m: HashMap<usize, Callback> = HashMap::from([
        (1, Box::new(|x: &mut usize| *x = 2) as Callback),
        (2, Box::new(|x: &mut usize| *x = 3) as Callback)
    ]);
    let default = |x: &mut usize| *x = 1;
    if m.get(&x).map(|func| func(&mut x)).is_none() {
        default(&mut x);
    }
    x
}

pub fn table_driven_vec_test(mut x: usize) -> usize {
    type Callback<'a> = &'a dyn Fn(&mut usize);
    let v: &[Callback<'_>] = &[
        &(|x: &mut usize| *x = 1) as Callback,
        &(|x: &mut usize| *x = 2) as Callback,
        &(|x: &mut usize| *x = 3) as Callback,
    ];
    let default = |x: &mut usize| *x = 1;
    if v.get(x).map(|func| func(&mut x)).is_none() {
        default(&mut x);
    }
    x
}
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn match_test(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:1:19: 1:24
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:1:36: 1:41

    bb0: {
        switchInt(_1) -> [1: bb2, 2: bb3, otherwise: bb1]; // scope 0 at src/lib.rs:2:5: 2:12
    }

    bb1: {
        _1 = const 1_usize;              // scope 0 at src/lib.rs:5:14: 5:19
        goto -> bb4;                     // scope 0 at src/lib.rs:5:14: 5:19
    }

    bb2: {
        _1 = const 2_usize;              // scope 0 at src/lib.rs:3:14: 3:19
        goto -> bb4;                     // scope 0 at src/lib.rs:3:14: 3:19
    }

    bb3: {
        _1 = const 3_usize;              // scope 0 at src/lib.rs:4:14: 4:19
        goto -> bb4;                     // scope 0 at src/lib.rs:4:14: 4:19
    }

    bb4: {
        _0 = _1;                         // scope 0 at src/lib.rs:7:5: 7:6
        return;                          // scope 0 at src/lib.rs:8:2: 8:2
    }
}

fn match_if_test(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:10:22: 10:27
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:10:39: 10:44
    let _2: usize;                       // in scope 0 at src/lib.rs:12:9: 12:10
    let _3: &usize;                      // in scope 0 at src/lib.rs:12:9: 12:10
    let mut _4: usize;                   // in scope 0 at src/lib.rs:12:14: 12:15
    let _5: usize;                       // in scope 0 at src/lib.rs:13:9: 13:10
    let _6: &usize;                      // in scope 0 at src/lib.rs:13:9: 13:10
    let mut _7: usize;                   // in scope 0 at src/lib.rs:13:14: 13:15
    scope 1 {
        debug y => _2;                   // in scope 1 at src/lib.rs:12:9: 12:10
        debug y => _3;                   // in scope 1 at src/lib.rs:12:9: 12:10
    }
    scope 2 {
        debug y => _5;                   // in scope 2 at src/lib.rs:13:9: 13:10
        debug y => _6;                   // in scope 2 at src/lib.rs:13:9: 13:10
    }

    bb0: {
        _3 = &_1;                        // scope 0 at src/lib.rs:12:9: 12:10
        _4 = (*_3);                      // scope 0 at src/lib.rs:12:14: 12:15
        switchInt(move _4) -> [1: bb1, otherwise: bb2]; // scope 0 at src/lib.rs:12:14: 12:20
    }

    bb1: {
        _2 = _1;                         // scope 0 at src/lib.rs:12:9: 12:10
        _1 = const 2_usize;              // scope 1 at src/lib.rs:12:24: 12:29
        goto -> bb5;                     // scope 0 at src/lib.rs:12:28: 12:29
    }

    bb2: {
        _6 = &_1;                        // scope 0 at src/lib.rs:13:9: 13:10
        _7 = (*_6);                      // scope 0 at src/lib.rs:13:14: 13:15
        switchInt(move _7) -> [2: bb3, otherwise: bb4]; // scope 0 at src/lib.rs:13:14: 13:20
    }

    bb3: {
        _5 = _1;                         // scope 0 at src/lib.rs:13:9: 13:10
        _1 = const 3_usize;              // scope 2 at src/lib.rs:13:24: 13:29
        goto -> bb5;                     // scope 0 at src/lib.rs:13:28: 13:29
    }

    bb4: {
        _1 = const 1_usize;              // scope 0 at src/lib.rs:14:14: 14:19
        goto -> bb5;                     // scope 0 at src/lib.rs:14:14: 14:19
    }

    bb5: {
        _0 = _1;                         // scope 0 at src/lib.rs:16:5: 16:6
        return;                          // scope 0 at src/lib.rs:17:2: 17:2
    }
}

fn if_else_test(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:19:21: 19:26
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:19:38: 19:43
    let mut _2: usize;                   // in scope 0 at src/lib.rs:20:8: 20:9
    let mut _3: usize;                   // in scope 0 at src/lib.rs:21:13: 21:14

    bb0: {
        _2 = _1;                         // scope 0 at src/lib.rs:20:8: 20:9
        switchInt(move _2) -> [1: bb1, otherwise: bb2]; // scope 0 at src/lib.rs:20:8: 20:14
    }

    bb1: {
        _1 = const 2_usize;              // scope 0 at src/lib.rs:20:17: 20:22
        goto -> bb5;                     // scope 0 at src/lib.rs:20:5: 22:19
    }

    bb2: {
        _3 = _1;                         // scope 0 at src/lib.rs:21:13: 21:14
        switchInt(move _3) -> [2: bb3, otherwise: bb4]; // scope 0 at src/lib.rs:21:13: 21:19
    }

    bb3: {
        _1 = const 3_usize;              // scope 0 at src/lib.rs:21:22: 21:27
        goto -> bb5;                     // scope 0 at src/lib.rs:21:10: 22:19
    }

    bb4: {
        _1 = const 1_usize;              // scope 0 at src/lib.rs:22:12: 22:17
        goto -> bb5;                     // scope 0 at src/lib.rs:21:10: 22:19
    }

    bb5: {
        _0 = _1;                         // scope 0 at src/lib.rs:23:5: 23:6
        return;                          // scope 0 at src/lib.rs:24:2: 24:2
    }
}

fn table_driven_hashmap_test(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:26:34: 26:39
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:26:51: 26:56
    let _2: std::collections::HashMap<usize, std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>>; // in scope 0 at src/lib.rs:29:9: 29:10
    let mut _3: [(usize, std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>); 2]; // in scope 0 at src/lib.rs:29:53: 32:6
    let mut _4: (usize, std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>); // in scope 0 at src/lib.rs:30:9: 30:58
    let mut _5: std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:30:13: 30:57
    let mut _6: std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:30:13: 30:57
    let mut _7: std::boxed::Box<[closure@src/lib.rs:30:22: 30:37]>; // in scope 0 at src/lib.rs:30:13: 30:45
    let mut _8: [closure@src/lib.rs:30:22: 30:37]; // in scope 0 at src/lib.rs:30:22: 30:44
    let mut _9: (usize, std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>); // in scope 0 at src/lib.rs:31:9: 31:58
    let mut _10: std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:31:13: 31:57
    let mut _11: std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:31:13: 31:57
    let mut _12: std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:31:13: 31:57
    let mut _13: std::boxed::Box<[closure@src/lib.rs:31:22: 31:37]>; // in scope 0 at src/lib.rs:31:13: 31:45
    let mut _14: [closure@src/lib.rs:31:22: 31:37]; // in scope 0 at src/lib.rs:31:22: 31:44
    let mut _16: bool;                   // in scope 0 at src/lib.rs:34:8: 34:52
    let mut _17: &std::option::Option<()>; // in scope 0 at src/lib.rs:34:8: 34:52
    let _18: std::option::Option<()>;    // in scope 0 at src/lib.rs:34:8: 34:42
    let mut _19: std::option::Option<&std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>>; // in scope 0 at src/lib.rs:34:8: 34:17
    let mut _20: &std::collections::HashMap<usize, std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>>; // in scope 0 at src/lib.rs:34:8: 34:17
    let mut _21: &usize;                 // in scope 0 at src/lib.rs:34:14: 34:16
    let _22: &usize;                     // in scope 0 at src/lib.rs:34:14: 34:16
    let mut _23: [closure@src/lib.rs:34:22: 34:28]; // in scope 0 at src/lib.rs:34:22: 34:41
    let mut _24: &mut usize;             // in scope 0 at src/lib.rs:34:22: 34:41
    let _25: ();                         // in scope 0 at src/lib.rs:35:9: 35:24
    let mut _26: &[closure@src/lib.rs:33:19: 33:34]; // in scope 0 at src/lib.rs:35:9: 35:16
    let mut _27: (&mut usize,);          // in scope 0 at src/lib.rs:35:9: 35:24
    let mut _28: &mut usize;             // in scope 0 at src/lib.rs:35:17: 35:23
    let mut _29: &mut usize;             // in scope 0 at src/lib.rs:35:17: 35:23
    scope 1 {
        debug m => _2;                   // in scope 1 at src/lib.rs:29:9: 29:10
        let _15: [closure@src/lib.rs:33:19: 33:34]; // in scope 1 at src/lib.rs:33:9: 33:16
        scope 2 {
            debug default => _15;        // in scope 2 at src/lib.rs:33:9: 33:16
        }
    }

    bb0: {
        _7 = Box::<[closure@src/lib.rs:30:22: 30:37]>::new(move _8) -> bb1; // scope 0 at src/lib.rs:30:13: 30:45
                                         // mir::Constant
                                         // + span: src/lib.rs:30:13: 30:21
                                         // + user_ty: UserType(2)
                                         // + literal: Const { ty: fn([closure@src/lib.rs:30:22: 30:37]) -> Box<[closure@src/lib.rs:30:22: 30:37]> {Box::<[closure@src/lib.rs:30:22: 30:37]>::new}, val: Value(<ZST>) }
    }

    bb1: {
        _6 = move _7 as std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)> (Pointer(Unsize)); // scope 0 at src/lib.rs:30:13: 30:45
        _5 = move _6;                    // scope 0 at src/lib.rs:30:13: 30:57
        _4 = (const 1_usize, move _5);   // scope 0 at src/lib.rs:30:9: 30:58
        _13 = Box::<[closure@src/lib.rs:31:22: 31:37]>::new(move _14) -> [return: bb2, unwind: bb11]; // scope 0 at src/lib.rs:31:13: 31:45
                                         // mir::Constant
                                         // + span: src/lib.rs:31:13: 31:21
                                         // + user_ty: UserType(4)
                                         // + literal: Const { ty: fn([closure@src/lib.rs:31:22: 31:37]) -> Box<[closure@src/lib.rs:31:22: 31:37]> {Box::<[closure@src/lib.rs:31:22: 31:37]>::new}, val: Value(<ZST>) }
    }

    bb2: {
        _12 = move _13 as std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)> (Pointer(Unsize)); // scope 0 at src/lib.rs:31:13: 31:45
        _11 = move _12;                  // scope 0 at src/lib.rs:31:13: 31:57
        _10 = move _11 as std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)> (Pointer(Unsize)); // scope 0 at src/lib.rs:31:13: 31:57
        _9 = (const 2_usize, move _10);  // scope 0 at src/lib.rs:31:9: 31:58
        _3 = [move _4, move _9];         // scope 0 at src/lib.rs:29:53: 32:6
        _2 = <HashMap<usize, Box<dyn for<'a> Fn(&'a mut usize)>> as From<[(usize, Box<dyn for<'a> Fn(&'a mut usize)>); 2]>>::from(move _3) -> bb3; // scope 0 at src/lib.rs:29:39: 32:7
                                         // mir::Constant
                                         // + span: src/lib.rs:29:39: 29:52
                                         // + user_ty: UserType(1)
                                         // + literal: Const { ty: fn([(usize, Box<dyn for<'a> Fn(&'a mut usize)>); 2]) -> HashMap<usize, Box<dyn for<'a> Fn(&'a mut usize)>> {<HashMap<usize, Box<dyn for<'a> Fn(&'a mut usize)>> as From<[(usize, Box<dyn for<'a> Fn(&'a mut usize)>); 2]>>::from}, val: Value(<ZST>) }
    }

    bb3: {
        _20 = &_2;                       // scope 2 at src/lib.rs:34:8: 34:17
        _22 = &_1;                       // scope 2 at src/lib.rs:34:14: 34:16
        _21 = _22;                       // scope 2 at src/lib.rs:34:14: 34:16
        _19 = HashMap::<usize, Box<dyn for<'a> Fn(&'a mut usize)>>::get::<usize>(move _20, move _21) -> [return: bb4, unwind: bb10]; // scope 2 at src/lib.rs:34:8: 34:17
                                         // mir::Constant
                                         // + span: src/lib.rs:34:10: 34:13
                                         // + literal: Const { ty: for<'a, 'b> fn(&'a HashMap<usize, Box<dyn for<'a> Fn(&'a mut usize)>>, &'b usize) -> Option<&'a Box<dyn for<'a> Fn(&'a mut usize)>> {HashMap::<usize, Box<dyn for<'a> Fn(&'a mut usize)>>::get::<usize>}, val: Value(<ZST>) }
    }

    bb4: {
        _24 = &mut _1;                   // scope 2 at src/lib.rs:34:22: 34:41
        _23 = [closure@src/lib.rs:34:22: 34:28] { x: move _24 }; // scope 2 at src/lib.rs:34:22: 34:41
                                         // closure
                                         // + def_id: DefId(0:12 ~ playground[2d3f]::table_driven_hashmap_test::{closure#3})
                                         // + substs: [
                                         //     i32,
                                         //     extern "rust-call" fn((&std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>,)),
                                         //     (&mut usize,),
                                         // ]
        _18 = Option::<&Box<dyn for<'a> Fn(&'a mut usize)>>::map::<(), [closure@src/lib.rs:34:22: 34:28]>(move _19, move _23) -> [return: bb5, unwind: bb10]; // scope 2 at src/lib.rs:34:8: 34:42
                                         // mir::Constant
                                         // + span: src/lib.rs:34:18: 34:21
                                         // + literal: Const { ty: fn(Option<&Box<dyn for<'a> Fn(&'a mut usize)>>, [closure@src/lib.rs:34:22: 34:28]) -> Option<()> {Option::<&Box<dyn for<'a> Fn(&'a mut usize)>>::map::<(), [closure@src/lib.rs:34:22: 34:28]>}, val: Value(<ZST>) }
    }

    bb5: {
        _17 = &_18;                      // scope 2 at src/lib.rs:34:8: 34:52
        _16 = Option::<()>::is_none(move _17) -> [return: bb6, unwind: bb10]; // scope 2 at src/lib.rs:34:8: 34:52
                                         // mir::Constant
                                         // + span: src/lib.rs:34:43: 34:50
                                         // + literal: Const { ty: for<'a> fn(&'a Option<()>) -> bool {Option::<()>::is_none}, val: Value(<ZST>) }
    }

    bb6: {
        switchInt(move _16) -> [0: bb8, otherwise: bb7]; // scope 2 at src/lib.rs:34:8: 34:52
    }

    bb7: {
        _26 = &_15;                      // scope 2 at src/lib.rs:35:9: 35:16
        _29 = &mut _1;                   // scope 2 at src/lib.rs:35:17: 35:23
        _28 = &mut (*_29);               // scope 2 at src/lib.rs:35:17: 35:23
        _27 = (move _28,);               // scope 2 at src/lib.rs:35:9: 35:24
        _25 = <[closure@src/lib.rs:33:19: 33:34] as Fn<(&mut usize,)>>::call(move _26, move _27) -> [return: bb13, unwind: bb10]; // scope 2 at src/lib.rs:35:9: 35:24
                                         // mir::Constant
                                         // + span: src/lib.rs:35:9: 35:16
                                         // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a [closure@src/lib.rs:33:19: 33:34], (&mut usize,)) -> <[closure@src/lib.rs:33:19: 33:34] as FnOnce<(&mut usize,)>>::Output {<[closure@src/lib.rs:33:19: 33:34] as Fn<(&mut usize,)>>::call}, val: Value(<ZST>) }
    }

    bb8: {
        _0 = _1;                         // scope 2 at src/lib.rs:37:5: 37:6
        drop(_2) -> bb9;                 // scope 0 at src/lib.rs:38:1: 38:2
    }

    bb9: {
        return;                          // scope 0 at src/lib.rs:38:2: 38:2
    }

    bb10 (cleanup): {
        drop(_2) -> bb12;                // scope 0 at src/lib.rs:38:1: 38:2
    }

    bb11 (cleanup): {
        drop(_4) -> bb12;                // scope 0 at src/lib.rs:32:5: 32:6
    }

    bb12 (cleanup): {
        resume;                          // scope 0 at src/lib.rs:26:1: 38:2
    }

    bb13: {
        goto -> bb8;                     // scope 2 at src/lib.rs:35:9: 35:24
    }
}

fn table_driven_hashmap_test::{closure#0}(_1: &[closure@src/lib.rs:30:22: 30:37], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:30:23: 30:24
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:30:38: 30:38

    bb0: {
        (*_2) = const 2_usize;           // scope 0 at src/lib.rs:30:38: 30:44
        return;                          // scope 0 at src/lib.rs:30:44: 30:44
    }
}

fn table_driven_hashmap_test::{closure#1}(_1: &[closure@src/lib.rs:31:22: 31:37], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:31:23: 31:24
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:31:38: 31:38

    bb0: {
        (*_2) = const 3_usize;           // scope 0 at src/lib.rs:31:38: 31:44
        return;                          // scope 0 at src/lib.rs:31:44: 31:44
    }
}

fn table_driven_hashmap_test::{closure#2}(_1: &[closure@src/lib.rs:33:19: 33:34], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:33:20: 33:21
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:33:35: 33:35

    bb0: {
        (*_2) = const 1_usize;           // scope 0 at src/lib.rs:33:35: 33:41
        return;                          // scope 0 at src/lib.rs:33:41: 33:41
    }
}

fn table_driven_hashmap_test::{closure#3}(_1: [closure@src/lib.rs:34:22: 34:28], _2: &Box<dyn for<'a> Fn(&'a mut usize)>) -> () {
    debug func => _2;                    // in scope 0 at src/lib.rs:34:23: 34:27
    debug x => (*(_1.0: &mut usize));    // in scope 0 at src/lib.rs:26:34: 26:39
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:34:29: 34:29
    let mut _3: &std::boxed::Box<dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:34:29: 34:33
    let mut _4: (&mut usize,);           // in scope 0 at src/lib.rs:34:29: 34:41
    let mut _5: &mut usize;              // in scope 0 at src/lib.rs:34:34: 34:40
    let mut _6: &mut usize;              // in scope 0 at src/lib.rs:34:34: 34:40
    let mut _7: &mut usize;              // in scope 0 at src/lib.rs:34:22: 34:41

    bb0: {
        _3 = _2;                         // scope 0 at src/lib.rs:34:29: 34:33
        _7 = deref_copy (_1.0: &mut usize); // scope 0 at src/lib.rs:34:34: 34:40
        _6 = &mut (*_7);                 // scope 0 at src/lib.rs:34:34: 34:40
        _5 = &mut (*_6);                 // scope 0 at src/lib.rs:34:34: 34:40
        _4 = (move _5,);                 // scope 0 at src/lib.rs:34:29: 34:41
        _0 = <Box<dyn for<'a> Fn(&'a mut usize)> as Fn<(&mut usize,)>>::call(move _3, move _4) -> bb1; // scope 0 at src/lib.rs:34:29: 34:41
                                         // mir::Constant
                                         // + span: src/lib.rs:34:29: 34:33
                                         // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a Box<dyn for<'a> Fn(&'a mut usize)>, (&mut usize,)) -> <Box<dyn for<'a> Fn(&'a mut usize)> as FnOnce<(&mut usize,)>>::Output {<Box<dyn for<'a> Fn(&'a mut usize)> as Fn<(&mut usize,)>>::call}, val: Value(<ZST>) }
    }

    bb1: {
        return;                          // scope 0 at src/lib.rs:34:41: 34:41
    }
}

fn table_driven_vec_test(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:40:30: 40:35
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:40:47: 40:52
    let _2: &[&dyn for<'a> std::ops::Fn(&'a mut usize)]; // in scope 0 at src/lib.rs:42:9: 42:10
    let mut _3: &[&dyn for<'a> std::ops::Fn(&'a mut usize); 3]; // in scope 0 at src/lib.rs:42:30: 46:6
    let _4: &[&dyn for<'a> std::ops::Fn(&'a mut usize); 3]; // in scope 0 at src/lib.rs:42:30: 46:6
    let mut _6: bool;                    // in scope 0 at src/lib.rs:48:8: 48:51
    let mut _7: &std::option::Option<()>; // in scope 0 at src/lib.rs:48:8: 48:51
    let _8: std::option::Option<()>;     // in scope 0 at src/lib.rs:48:8: 48:41
    let mut _9: std::option::Option<&&dyn for<'a> std::ops::Fn(&'a mut usize)>; // in scope 0 at src/lib.rs:48:8: 48:16
    let mut _10: &[&dyn for<'a> std::ops::Fn(&'a mut usize)]; // in scope 0 at src/lib.rs:48:8: 48:16
    let mut _11: usize;                  // in scope 0 at src/lib.rs:48:14: 48:15
    let mut _12: [closure@src/lib.rs:48:21: 48:27]; // in scope 0 at src/lib.rs:48:21: 48:40
    let mut _13: &mut usize;             // in scope 0 at src/lib.rs:48:21: 48:40
    let _14: ();                         // in scope 0 at src/lib.rs:49:9: 49:24
    let mut _15: &[closure@src/lib.rs:47:19: 47:34]; // in scope 0 at src/lib.rs:49:9: 49:16
    let mut _16: (&mut usize,);          // in scope 0 at src/lib.rs:49:9: 49:24
    let mut _17: &mut usize;             // in scope 0 at src/lib.rs:49:17: 49:23
    let mut _18: &mut usize;             // in scope 0 at src/lib.rs:49:17: 49:23
    let mut _19: &[&dyn for<'a> std::ops::Fn(&'a mut usize); 3]; // in scope 0 at src/lib.rs:42:30: 46:6
    scope 1 {
        debug v => _2;                   // in scope 1 at src/lib.rs:42:9: 42:10
        let _5: [closure@src/lib.rs:47:19: 47:34]; // in scope 1 at src/lib.rs:47:9: 47:16
        scope 2 {
            debug default => _5;         // in scope 2 at src/lib.rs:47:9: 47:16
        }
    }

    bb0: {
        _19 = const _;                   // scope 0 at src/lib.rs:42:30: 46:6
                                         // mir::Constant
                                         // + span: src/lib.rs:42:30: 46:6
                                         // + literal: Const { ty: &[&dyn for<'a> Fn(&'a mut usize); 3], val: Unevaluated(table_driven_vec_test, [], Some(promoted[0])) }
        _4 = _19;                        // scope 0 at src/lib.rs:42:30: 46:6
        _3 = _4;                         // scope 0 at src/lib.rs:42:30: 46:6
        _2 = move _3 as &[&dyn for<'a> std::ops::Fn(&'a mut usize)] (Pointer(Unsize)); // scope 0 at src/lib.rs:42:30: 46:6
        _10 = _2;                        // scope 2 at src/lib.rs:48:8: 48:16
        _11 = _1;                        // scope 2 at src/lib.rs:48:14: 48:15
        _9 = core::slice::<impl [&dyn for<'a> Fn(&'a mut usize)]>::get::<usize>(move _10, move _11) -> bb1; // scope 2 at src/lib.rs:48:8: 48:16
                                         // mir::Constant
                                         // + span: src/lib.rs:48:10: 48:13
                                         // + literal: Const { ty: for<'a> fn(&'a [&dyn for<'a> Fn(&'a mut usize)], usize) -> Option<&'a <usize as SliceIndex<[&dyn for<'a> Fn(&'a mut usize)]>>::Output> {core::slice::<impl [&dyn for<'a> Fn(&'a mut usize)]>::get::<usize>}, val: Value(<ZST>) }
    }

    bb1: {
        _13 = &mut _1;                   // scope 2 at src/lib.rs:48:21: 48:40
        _12 = [closure@src/lib.rs:48:21: 48:27] { x: move _13 }; // scope 2 at src/lib.rs:48:21: 48:40
                                         // closure
                                         // + def_id: DefId(0:20 ~ playground[2d3f]::table_driven_vec_test::{closure#4})
                                         // + substs: [
                                         //     i32,
                                         //     extern "rust-call" fn((&&dyn for<'a> std::ops::Fn(&'a mut usize),)),
                                         //     (&mut usize,),
                                         // ]
        _8 = Option::<&&dyn for<'a> Fn(&'a mut usize)>::map::<(), [closure@src/lib.rs:48:21: 48:27]>(move _9, move _12) -> bb2; // scope 2 at src/lib.rs:48:8: 48:41
                                         // mir::Constant
                                         // + span: src/lib.rs:48:17: 48:20
                                         // + literal: Const { ty: fn(Option<&&dyn for<'a> Fn(&'a mut usize)>, [closure@src/lib.rs:48:21: 48:27]) -> Option<()> {Option::<&&dyn for<'a> Fn(&'a mut usize)>::map::<(), [closure@src/lib.rs:48:21: 48:27]>}, val: Value(<ZST>) }
    }

    bb2: {
        _7 = &_8;                        // scope 2 at src/lib.rs:48:8: 48:51
        _6 = Option::<()>::is_none(move _7) -> bb3; // scope 2 at src/lib.rs:48:8: 48:51
                                         // mir::Constant
                                         // + span: src/lib.rs:48:42: 48:49
                                         // + literal: Const { ty: for<'a> fn(&'a Option<()>) -> bool {Option::<()>::is_none}, val: Value(<ZST>) }
    }

    bb3: {
        switchInt(move _6) -> [0: bb5, otherwise: bb4]; // scope 2 at src/lib.rs:48:8: 48:51
    }

    bb4: {
        _15 = &_5;                       // scope 2 at src/lib.rs:49:9: 49:16
        _18 = &mut _1;                   // scope 2 at src/lib.rs:49:17: 49:23
        _17 = &mut (*_18);               // scope 2 at src/lib.rs:49:17: 49:23
        _16 = (move _17,);               // scope 2 at src/lib.rs:49:9: 49:24
        _14 = <[closure@src/lib.rs:47:19: 47:34] as Fn<(&mut usize,)>>::call(move _15, move _16) -> bb5; // scope 2 at src/lib.rs:49:9: 49:24
                                         // mir::Constant
                                         // + span: src/lib.rs:49:9: 49:16
                                         // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a [closure@src/lib.rs:47:19: 47:34], (&mut usize,)) -> <[closure@src/lib.rs:47:19: 47:34] as FnOnce<(&mut usize,)>>::Output {<[closure@src/lib.rs:47:19: 47:34] as Fn<(&mut usize,)>>::call}, val: Value(<ZST>) }
    }

    bb5: {
        _0 = _1;                         // scope 2 at src/lib.rs:51:5: 51:6
        return;                          // scope 0 at src/lib.rs:52:2: 52:2
    }
}

promoted[0] in table_driven_vec_test: &[&dyn for<'a> Fn(&'a mut usize); 3] = {
    let mut _0: &[&dyn for<'a> std::ops::Fn(&'a mut usize); 3]; // return place in scope 0 at src/lib.rs:42:30: 46:6
    let mut _1: [&dyn for<'a> std::ops::Fn(&'a mut usize); 3]; // in scope 0 at src/lib.rs:42:31: 46:6
    let mut _2: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:43:9: 43:46
    let mut _3: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:43:9: 43:46
    let mut _4: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:43:9: 43:46
    let mut _5: &[closure@src/lib.rs:43:11: 43:26]; // in scope 0 at src/lib.rs:43:9: 43:34
    let mut _6: &[closure@src/lib.rs:43:11: 43:26]; // in scope 0 at src/lib.rs:43:9: 43:34
    let mut _7: [closure@src/lib.rs:43:11: 43:26]; // in scope 0 at src/lib.rs:43:10: 43:34
    let mut _8: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:44:9: 44:46
    let mut _9: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:44:9: 44:46
    let mut _10: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:44:9: 44:46
    let mut _11: &[closure@src/lib.rs:44:11: 44:26]; // in scope 0 at src/lib.rs:44:9: 44:34
    let mut _12: &[closure@src/lib.rs:44:11: 44:26]; // in scope 0 at src/lib.rs:44:9: 44:34
    let mut _13: [closure@src/lib.rs:44:11: 44:26]; // in scope 0 at src/lib.rs:44:10: 44:34
    let mut _14: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:45:9: 45:46
    let mut _15: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:45:9: 45:46
    let mut _16: &dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:45:9: 45:46
    let mut _17: &[closure@src/lib.rs:45:11: 45:26]; // in scope 0 at src/lib.rs:45:9: 45:34
    let mut _18: &[closure@src/lib.rs:45:11: 45:26]; // in scope 0 at src/lib.rs:45:9: 45:34
    let mut _19: [closure@src/lib.rs:45:11: 45:26]; // in scope 0 at src/lib.rs:45:10: 45:34

    bb0: {
        _7 = [closure@src/lib.rs:43:11: 43:26]; // scope 0 at src/lib.rs:43:10: 43:34
                                         // closure
                                         // + def_id: DefId(0:16 ~ playground[2d3f]::table_driven_vec_test::{closure#0})
                                         // + substs: [
                                         //     i8,
                                         //     for<'a> extern "rust-call" fn((&'a mut usize,)),
                                         //     (),
                                         // ]
        _6 = &_7;                        // scope 0 at src/lib.rs:43:9: 43:34
        _5 = &(*_6);                     // scope 0 at src/lib.rs:43:9: 43:34
        _4 = move _5 as &dyn for<'a> std::ops::Fn(&'a mut usize) (Pointer(Unsize)); // scope 0 at src/lib.rs:43:9: 43:34
        _3 = &(*_4);                     // scope 0 at src/lib.rs:43:9: 43:46
        _2 = move _3 as &dyn for<'a> std::ops::Fn(&'a mut usize) (Pointer(Unsize)); // scope 0 at src/lib.rs:43:9: 43:46
        _13 = [closure@src/lib.rs:44:11: 44:26]; // scope 0 at src/lib.rs:44:10: 44:34
                                         // closure
                                         // + def_id: DefId(0:17 ~ playground[2d3f]::table_driven_vec_test::{closure#1})
                                         // + substs: [
                                         //     i8,
                                         //     for<'a> extern "rust-call" fn((&'a mut usize,)),
                                         //     (),
                                         // ]
        _12 = &_13;                      // scope 0 at src/lib.rs:44:9: 44:34
        _11 = &(*_12);                   // scope 0 at src/lib.rs:44:9: 44:34
        _10 = move _11 as &dyn for<'a> std::ops::Fn(&'a mut usize) (Pointer(Unsize)); // scope 0 at src/lib.rs:44:9: 44:34
        _9 = &(*_10);                    // scope 0 at src/lib.rs:44:9: 44:46
        _8 = move _9 as &dyn for<'a> std::ops::Fn(&'a mut usize) (Pointer(Unsize)); // scope 0 at src/lib.rs:44:9: 44:46
        _19 = [closure@src/lib.rs:45:11: 45:26]; // scope 0 at src/lib.rs:45:10: 45:34
                                         // closure
                                         // + def_id: DefId(0:18 ~ playground[2d3f]::table_driven_vec_test::{closure#2})
                                         // + substs: [
                                         //     i8,
                                         //     for<'a> extern "rust-call" fn((&'a mut usize,)),
                                         //     (),
                                         // ]
        _18 = &_19;                      // scope 0 at src/lib.rs:45:9: 45:34
        _17 = &(*_18);                   // scope 0 at src/lib.rs:45:9: 45:34
        _16 = move _17 as &dyn for<'a> std::ops::Fn(&'a mut usize) (Pointer(Unsize)); // scope 0 at src/lib.rs:45:9: 45:34
        _15 = &(*_16);                   // scope 0 at src/lib.rs:45:9: 45:46
        _14 = move _15 as &dyn for<'a> std::ops::Fn(&'a mut usize) (Pointer(Unsize)); // scope 0 at src/lib.rs:45:9: 45:46
        _1 = [move _2, move _8, move _14]; // scope 0 at src/lib.rs:42:31: 46:6
        _0 = &_1;                        // scope 0 at src/lib.rs:42:30: 46:6
        return;                          // scope 0 at src/lib.rs:42:30: 46:6
    }
}

fn table_driven_vec_test::{closure#0}(_1: &[closure@src/lib.rs:43:11: 43:26], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:43:12: 43:13
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:43:27: 43:27

    bb0: {
        (*_2) = const 1_usize;           // scope 0 at src/lib.rs:43:27: 43:33
        return;                          // scope 0 at src/lib.rs:43:34: 43:34
    }
}

fn table_driven_vec_test::{closure#1}(_1: &[closure@src/lib.rs:44:11: 44:26], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:44:12: 44:13
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:44:27: 44:27

    bb0: {
        (*_2) = const 2_usize;           // scope 0 at src/lib.rs:44:27: 44:33
        return;                          // scope 0 at src/lib.rs:44:34: 44:34
    }
}

fn table_driven_vec_test::{closure#2}(_1: &[closure@src/lib.rs:45:11: 45:26], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:45:12: 45:13
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:45:27: 45:27

    bb0: {
        (*_2) = const 3_usize;           // scope 0 at src/lib.rs:45:27: 45:33
        return;                          // scope 0 at src/lib.rs:45:34: 45:34
    }
}

fn table_driven_vec_test::{closure#3}(_1: &[closure@src/lib.rs:47:19: 47:34], _2: &mut usize) -> () {
    debug x => _2;                       // in scope 0 at src/lib.rs:47:20: 47:21
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:47:35: 47:35

    bb0: {
        (*_2) = const 1_usize;           // scope 0 at src/lib.rs:47:35: 47:41
        return;                          // scope 0 at src/lib.rs:47:41: 47:41
    }
}

fn table_driven_vec_test::{closure#4}(_1: [closure@src/lib.rs:48:21: 48:27], _2: &&dyn for<'a> Fn(&'a mut usize)) -> () {
    debug func => _2;                    // in scope 0 at src/lib.rs:48:22: 48:26
    debug x => (*(_1.0: &mut usize));    // in scope 0 at src/lib.rs:40:30: 40:35
    let mut _0: ();                      // return place in scope 0 at src/lib.rs:48:28: 48:28
    let mut _3: &&dyn for<'a> std::ops::Fn(&'a mut usize); // in scope 0 at src/lib.rs:48:28: 48:32
    let mut _4: (&mut usize,);           // in scope 0 at src/lib.rs:48:28: 48:40
    let mut _5: &mut usize;              // in scope 0 at src/lib.rs:48:33: 48:39
    let mut _6: &mut usize;              // in scope 0 at src/lib.rs:48:33: 48:39
    let mut _7: &mut usize;              // in scope 0 at src/lib.rs:48:21: 48:40

    bb0: {
        _3 = _2;                         // scope 0 at src/lib.rs:48:28: 48:32
        _7 = deref_copy (_1.0: &mut usize); // scope 0 at src/lib.rs:48:33: 48:39
        _6 = &mut (*_7);                 // scope 0 at src/lib.rs:48:33: 48:39
        _5 = &mut (*_6);                 // scope 0 at src/lib.rs:48:33: 48:39
        _4 = (move _5,);                 // scope 0 at src/lib.rs:48:28: 48:40
        _0 = <&dyn for<'a> Fn(&'a mut usize) as Fn<(&mut usize,)>>::call(move _3, move _4) -> bb1; // scope 0 at src/lib.rs:48:28: 48:40
                                         // mir::Constant
                                         // + span: src/lib.rs:48:28: 48:32
                                         // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a &dyn for<'a> Fn(&'a mut usize), (&mut usize,)) -> <&dyn for<'a> Fn(&'a mut usize) as FnOnce<(&mut usize,)>>::Output {<&dyn for<'a> Fn(&'a mut usize) as Fn<(&mut usize,)>>::call}, val: Value(<ZST>) }
    }

    bb1: {
        return;                          // scope 0 at src/lib.rs:48:40: 48:40
    }
}
作者 lithbitren 2023-02-04 21:51

会不会是match存在栈上,所以比较快?

--
👇
lithbitren: 觉得还是有点问题,我本来以为vec表驱动会和match差不多,但结果上match怎么表现这么好

作者 lithbitren 2023-02-02 15:44

(⊙o⊙)哇,什么场景需要手写两三千个分支,求大佬分享分享开开眼。

如果是栈溢出的话,开线程似乎可以设置stack_size控制子线程栈大小,或者在cargo运行时命令行设置设置RUST_MIN_STACK,不过没试过不太清楚行不行。

或者试试分级match,如果函数能行的话,可以每n个分支打包进一个分支或一个函数,应该能比hashmap快不少吧。

--
👇
gant boam: 我之前有场景是大概两三千个条件,我用match无法编译成功,栈溢出还是啥错误来着,我只能改成hashmap+fn了,我还是希望高性能,不知道有啥方法使用matxh不

gant boam 2023-02-02 00:24

我之前有场景是大概两三千个条件,我用match无法编译成功,栈溢出还是啥错误来着,我只能改成hashmap+fn了,我还是希望高性能,不知道有啥方法使用matxh不

作者 lithbitren 2023-02-01 17:42

觉得还是有点问题,我本来以为vec表驱动会和match差不多,但结果上match怎么表现这么好

作者 lithbitren 2023-01-31 22:11

俺比较菜,不太爱标生命周期,遇事不决都上box,但标生命周期确实比box更简洁,不过测出来性能似乎差不多。

因为用宏不知道怎么批量生产不同名的外部函数,于是还是继续用闭包写了,其实用普通函数更好写,如果出入参数类型统一的话甚至都不用重命名类型,也不用as类型断言,闭包反而类型难以统一所以每次定义都得类型断言。

之前测的时候也考虑过数组表驱动,不过,讲实话以前真没见过其他语言的数组表驱动,大多数表驱动都是匹配字符串,纯数字匹配比较罕见。但这个测试如果用字符串来匹配的话,匹配开销比分支选择大不少,对测试分支本身的耗时感觉会不太明显。

--
👇
wangbyby: 表驱动的话,用数组代替HashMap尝试下?

wangbyby 2023-01-30 10:43

表驱动的话,用数组代替HashMap尝试下?

fn fn1(x: &mut usize){
    *x = 1;
}

fn fn2(x: &mut usize){
    *x = 2;
}


fn table_driven_test_fnpointer(n: usize, mut x: usize) -> usize {

    type Callback = fn(&mut usize);
   
    let m: &[Callback] = &[
        fn1 as Callback,
        fn2 as Callback,
        
    ];
    let default = |x: &mut usize| *x = 1;
    for _ in 0..n {
        if m.get(x).map(|func| func(&mut x)).is_none() {
            // 当 x 不等于 1 到 63 的时候,x = 1;
            default(&mut x);
        }
    }
    x
}


fn table_driven_test_fntrait(n: usize, mut x: usize) -> usize {
    type Callback<'a> = &'a dyn Fn(&mut usize);
   
    let m: &[Callback<'_>] = &[
        &fn1 as Callback,
        &fn2 as Callback,
    ];
    
    let default = |x: &mut usize| *x = 1;
    for _ in 0..n {
        if m.get(x).map(|func| func(&mut x)).is_none() {
            // 当 x 不等于 1 到 63 的时候,x = 1;
            default(&mut x);
        }
    }
    x
}
1 共 9 条评论, 1 页