AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79425528
Accepted
NoBullsh1t
NoBullsh1t
Asked: 2025-02-10 03:05:25 +0800 CST2025-02-10 03:05:25 +0800 CST 2025-02-10 03:05:25 +0800 CST

创建闭包与创建结构的性能损失是多少?

  • 772

我做了一些看起来很聪明的事情。为了更好地处理错误,我编写了如下 DB 操作

    fn handle_delete(&self, id: &Id) -> Result<(), SqliteError> {
        db_op(
            || DbOperationInfo::new("filerequests", OperationType::Delete),
            || {
                self.conn
                    .execute("DELETE FROM filerequests WHERE id = ?", [id.to_string()])?;
                Ok(())
            },
        )
    }

我的想法是,我有我的 SqliteError 枚举,它包含一个相当小的结构,其中包含有关尝试的操作及其rusqlite::error::Error原因的一些信息。

该db_op函数的作用是接受两个闭包,第一个闭包生成包含操作信息的小结构,第二个闭包执行实际操作。如果(且仅当)发生错误,则执行第一个闭包生成操作信息,然后添加导致错误的代码以创建SqliteError。

因此操作信息的创建是懒惰的。

但是我在这里真的有收获吗?还是创建闭包的成本同样高昂?或者,也许编译器会优化闭包创建并只执行一次?或者也许它会同样优化结构创建(内容有点静态)?

在此寻求一些指导:这种优化(可能)真的有帮助吗?

performance
  • 1 1 个回答
  • 49 Views

1 个回答

  • Voted
  1. Best Answer
    Finomnis
    2025-02-10T04:21:33+08:002025-02-10T04:21:33+08:00

    divan您可以使用(或 Rust 中的任何其他基准测试工具,如)对这种情况进行基准测试criterion。

    您需要先最小化代码以隔离要测试的实际代码。然后在正确的位置添加一堆black_box以避免测试代码被编译掉。

    这是我试图最小化和基准化您的情况。请注意,我不知道 中到底发生了什么DbOperationInfo::new(),所以我只是String在其中进行了分配,以演示某种基于堆的初始化。如果DbOperationInfo不执行堆分配,结果可能不太明显。

    #![allow(dead_code)]
    
    use std::hint::black_box;
    
    enum OperationType {
        Delete,
        Insert,
    }
    
    struct DbOperationInfo {
        s: String,
        t: OperationType,
    }
    
    impl DbOperationInfo {
        pub fn new(s: impl Into<String>, t: OperationType) -> Self {
            Self { s: s.into(), t }
        }
    }
    
    fn db_op_closure(generate: bool, f: impl FnOnce() -> DbOperationInfo) {
        if black_box(generate) {
            black_box(f());
        }
    }
    
    fn db_op_value(generate: bool, val: DbOperationInfo) {
        if black_box(generate) {
            black_box(val);
        }
    }
    
    #[divan::bench(args = [true, false], min_time = 0.5)]
    fn handle_delete_closure(generate_value: bool) {
        db_op_closure(generate_value, || {
            DbOperationInfo::new("filerequests", OperationType::Delete)
        })
    }
    
    #[divan::bench(args = [true, false], min_time = 0.5)]
    fn handle_delete_value(generate_value: bool) {
        db_op_value(
            generate_value,
            DbOperationInfo::new("filerequests", OperationType::Delete),
        )
    }
    
    fn main() {
        // Run registered benchmarks.
        divan::main();
    }
    

    结果如下:

    closure_vs_direct         fastest       │ slowest       │ median        │ mean          │ samples │ iters
    ├─ handle_delete_closure                │               │               │               │         │
    │  ├─ false               1.011 ns      │ 5.796 ns      │ 1.023 ns      │ 1.04 ns       │ 22881   │ 187441152
    │  ╰─ true                28.83 ns      │ 476.6 ns      │ 29.41 ns      │ 31.12 ns      │ 29588   │ 15149056
    ╰─ handle_delete_value                  │               │               │               │         │
       ├─ false               28.24 ns      │ 194.2 ns      │ 29.02 ns      │ 30.34 ns      │ 60225   │ 15417600
       ╰─ true                28.63 ns      │ 114.7 ns      │ 29.41 ns      │ 30.54 ns      │ 30153   │ 15438336
    

    那么这意味着什么?

    • 我的版本DbOperationInfo::new()大约需要 28 纳秒。
    • 当操作数为闭包且闭包没有被调用时,时间仅为 1 纳秒。

    这意味着单个堆分配就足以使其值得用闭包包装。


    为了看到区别,我从中删除了堆分配DbOperationInfo::new():

    struct DbOperationInfo {
        s: &'static str,
        t: OperationType,
    }
    
    impl DbOperationInfo {
        pub fn new(s: &'static str, t: OperationType) -> Self {
            Self { s, t }
        }
    }
    

    现在你是否将其包装在闭包中已经不重要了:

    closure_vs_direct         fastest       │ slowest       │ median        │ mean          │ samples │ iters
    ├─ handle_delete_closure                │               │               │               │         │
    │  ├─ false               1.291 ns      │ 122 ns        │ 1.303 ns      │ 1.387 ns      │ 19944   │ 163381248
    │  ╰─ true                1.291 ns      │ 7.199 ns      │ 1.303 ns      │ 1.36 ns       │ 20790   │ 170311680
    ╰─ handle_delete_value                  │               │               │               │         │
       ├─ false               1.291 ns      │ 6.955 ns      │ 1.303 ns      │ 1.371 ns      │ 20484   │ 167804928
       ╰─ true                1.291 ns      │ 22.99 ns      │ 1.303 ns      │ 1.367 ns      │ 20653   │ 169189376
    

    这告诉我,只要你有一个堆分配或某种计算,就把它包装在一个闭包中。

    如果你的对象很小并且可以静态分配,那就没关系了。理论上,在这种情况下删除闭包可能会稍微快一点,但编译器可能会优化闭包以使其相同。

    • 2

相关问题

  • 提高斯特林数计算效率

  • Haskell 速度问题,执行程序的两个部分比单独执行任一部分花费的时间要长得多

  • 为什么优化后的 g 脚本代码比“低效”的代码慢?

  • 如何让JMeter用户不等待响应

  • 如何解释两个处理器之间巨大的执行速度差异?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve