gpt4 book ai didi

rust - 如何在不降低性能的情况下使用 filter_map() 而不是 filter() 与 map() 结合使用?

转载 作者:行者123 更新时间:2023-12-03 11:25:05 27 4
gpt4 key购买 nike

我想用 filter_map()而不是 unwrap()map()filter()但我看到这样做时性能下降。我如何使用 filter_map() 编写代码不损失性能?为什么首先会损失性能?

src/lib.rs

use std::collections::HashMap;

pub enum Kind {
Square(Square),
Circle(Circle),
}

#[derive(Default, Copy, Clone)]
pub struct Circle {
a: u32,
b: u32,
c: u32,
d: u32,
}

#[derive(Default)]
pub struct Square {
a: u32,
b: Option<u32>,
c: Option<u32>,
d: Option<u32>,
e: Option<u32>,
}

impl Kind {
pub fn get_circle(&self) -> Option<&Circle> {
if let Kind::Circle(b) = self {
return Some(b);
}
None
}
}

长凳/test.rs
#![feature(test)]
extern crate test;

#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use test::Bencher;
use testing::Circle;
use testing::Kind;
use testing::Square;

fn get_bencher() -> HashMap<SocketAddr, Kind> {
let mut question = HashMap::new();
let square: Square = Default::default();
question.insert(
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0),
Kind::Square(square),
);

let circle: Circle = Default::default();
for n in 1..=10000 {
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), n);
question.insert(socket, Kind::Circle(circle));
}
question
}

#[bench]
fn bencher01(b: &mut Bencher) {
let question = get_bencher();

b.iter(|| {
question
.iter()
.map(|a| (a.0, a.1.get_circle()))
.filter_map(|(&a, b)| Some((a, b?)))
.collect::<Vec<_>>()
})
}

#[bench]
fn bencher02(b: &mut Bencher) {
let question = get_bencher();

b.iter(|| {
question
.iter()
.map(|a| (a.0, a.1.get_circle()))
.filter(|c| c.1.is_some())
.map(|d| (*d.0, d.1.unwrap()))
.collect::<Vec<_>>()
})
}

#[bench]
fn bencher03(b: &mut Bencher) {
let question = get_bencher();

b.iter(|| {
question
.iter()
.filter_map(|a| Some((*a.0, a.1.get_circle()?)))
.collect::<Vec<_>>()
})
}
}

每晚使用 Rust 和 cargo bench 运行这些测试强制 Release模式。

输出

running 3 tests
test tests::bencher01 ... bench: 201,978 ns/iter (+/- 12,787)
test tests::bencher02 ... bench: 89,004 ns/iter (+/- 6,204)
test tests::bencher03 ... bench: 238,569 ns/iter (+/- 6,004)

我正在使用 rustc 1.44.0-nightly (6dee5f112 2020-04-06)Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz Linux #### 5.6.4-arch1-1 #1 SMP PREEMPT Mon, 13 Apr 2020 12:21:19 +0000 x86_64 GNU/Linux

最佳答案

不同之处在于,在您的 flat_map 中实现,你复制 SocketAddr在你检查形状是否是圆形之前,所以当形状不是圆形时,你浪费时间复制和丢弃它。看:

#[bench]
fn bencher04(b: &mut Bencher) {
let question = get_bencher();

b.iter(|| {
question
.iter()
.filter_map(|a| {
let c = a.1.get_circle()?;
Some((*a.0, c))
})
.collect::<Vec<_>>()
})
}

这给了我:

running 4 tests
test tests::bencher01 ... bench: 339,720 ns/iter (+/- 23,464)
test tests::bencher02 ... bench: 329,727 ns/iter (+/- 12,212)
test tests::bencher03 ... bench: 335,785 ns/iter (+/- 16,195)
test tests::bencher04 ... bench: 327,622 ns/iter (+/- 20,807)

注意:我的差异比你的小,因为我在 32 位平台上运行,在该平台上复制 SocketAddr快得多。

在 64 位平台上, bencher04表现不佳。看着 generated assembly , bencher04看起来与 bencher02 非常相似但由于某种原因,它确实移动了更多数据。

然而:
#[bench]
fn bencher05(b: &mut Bencher) {
let question = get_bencher();

b.iter(|| {
question
.iter()
.flat_map(|a| {
a.1.get_circle().map (|c| (*a.0, c))
})
.collect::<Vec<_>>()
})
}

性能更接近:

running 5 tests
test tests::bencher01 ... bench: 219,381 ns/iter (+/- 10,186)
test tests::bencher02 ... bench: 148,273 ns/iter (+/- 3,068)
test tests::bencher03 ... bench: 244,614 ns/iter (+/- 3,057)
test tests::bencher04 ... bench: 209,905 ns/iter (+/- 7,509)
test tests::bencher05 ... bench: 167,143 ns/iter (+/- 5,029)

关于rust - 如何在不降低性能的情况下使用 filter_map() 而不是 filter() 与 map() 结合使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61471978/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com