在计算中使用布尔值以避免分支

2023-11-21

这是我想出的一些微观优化的好奇心:

struct Timer {
    bool running{false};
    int ticks{0};

    void step_versionOne(int mStepSize) {
        if(running) ticks += mStepSize;
    }

    void step_versionTwo(int mStepSize) {
        ticks += mStepSize * static_cast<int>(running);
    }
};

看起来这两种方法实际上做了同样的事情。第二个版本是否避免了分支(因此比第一个版本更快),或者任何编译器都能够执行这种优化-O3?


是的,你的技巧可以避免分支并且使它变得更快......有时。

我编写了基准测试来比较各种情况下的这些解决方案以及我自己的解决方案:

ticks += mStepSize & -static_cast<int>(running)

我的结果如下:

Off:
 branch: 399949150
 mul:    399940271
 andneg: 277546678
On:
 branch: 204035423
 mul:    399937142
 andneg: 277581853
Pattern:
 branch: 327724860
 mul:    400010363
 andneg: 277551446
Random:
 branch: 915235440
 mul:    399916440
 andneg: 277537411

Off是定时器关闭的时候。在这种情况下,解决方案大约需要相同的时间。

On当它们被打开时。分支解决方案速度提高两倍。

Pattern是当它们处于 100110 模式时。性能相似,但分支速度更快一些。

Random是分支不可预测的时候。在这种情况下,乘法速度快了 2 倍以上。

在所有情况下,我的位黑客技巧都是最快的,除了On分支获胜。

请注意,此基准测试不一定代表所有编译器版本处理器等。即使基准测试的微小变化也可能使结果颠倒(例如,如果编译器可以内联知道mStepSize is 1比乘法实际上可能是最快的)。

基准测试代码:

#include<array>
#include<iostream>
#include<chrono>

struct Timer {
    bool running{false};
    int ticks{0};

    void branch(int mStepSize) {
        if(running) ticks += mStepSize;
    }

    void mul(int mStepSize) {
        ticks += mStepSize * static_cast<int>(running);
    }

    void andneg(int mStepSize) {
        ticks += mStepSize & -static_cast<int>(running);
    }
};

void run(std::array<Timer, 256>& timers, int step) {
    auto start = std::chrono::steady_clock::now();
    for(int i = 0; i < 1000000; i++)
        for(auto& t : timers)
            t.branch(step);
    auto end = std::chrono::steady_clock::now();
    std::cout << "branch: " << (end - start).count() << std::endl;
    start = std::chrono::steady_clock::now();
    for(int i = 0; i < 1000000; i++)
        for(auto& t : timers)
            t.mul(step);
    end = std::chrono::steady_clock::now();
    std::cout << "mul:    " << (end - start).count() << std::endl;
    start = std::chrono::steady_clock::now();
    for(int i = 0; i < 1000000; i++)
        for(auto& t : timers)
            t.andneg(step);
    end = std::chrono::steady_clock::now();
    std::cout << "andneg: " << (end - start).count() << std::endl;
}

int main() {
    std::array<Timer, 256> timers;
    int step = rand() % 256;

    run(timers, step); // warm up
    std::cout << "Off:\n";
    run(timers, step);
    for(auto& t : timers)
        t.running = true;
    std::cout << "On:\n";
    run(timers, step);
    std::array<bool, 6> pattern = {1, 0, 0, 1, 1, 0};
    for(int i = 0; i < 256; i++)
        timers[i].running = pattern[i % 6];
    std::cout << "Pattern:\n";
    run(timers, step);
    for(auto& t : timers)
        t.running = rand()&1;
    std::cout << "Random:\n";
    run(timers, step);
    for(auto& t : timers)
        std::cout << t.ticks << ' ';
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在计算中使用布尔值以避免分支 的相关文章

随机推荐

  • 在 Delphi XE 中构建事件宏

    根据Delphi的帮助文件 当我打开对话框将构建事件添加到项目选项时 该对话框应显示我可以在构建事件的命令行上使用的宏 占位符 列表 当我在 Delphi XE 中尝试此操作时 宏列表为空 帮助文件也没有说明哪些宏可用 我可以找到 那么 哪
  • 如何将字符串转换为字典或列表?

    我有诸如以下的字符串 1 2 3 and a 1 b 2 如何将它们转换为列表 字典 有人提到ast literal eval or eval可以解析转换为列表 字典的字符串 有什么区别ast literal eval and eval a
  • Firestore - 检查模块与服务器的连接状态

    我注意到关闭互联网连接并重新打开后 while my Android app仍在运行 无论是否在后台 则需要Firestore模块需要很长时间才能重新获得与服务器的连接 大约一分钟 并且我无法进行任何操作Firestore操作直到恢复连接
  • 如何将 groupby.first() 与变换函数一起使用

    我想使用 groupby first 函数查找组的第一个非空值并将该值转换为组中的每一行 我尝试过以下代码 import pandas as pd import numpy as np raw data col1 a a a b b b b
  • MATCH AGAINST 和 LIKE 哪个 SQL 查询更好?

    要在数据库中搜索 foo desc 和 bar desc 任意列中同时具有关键字 foo 和 bar 的行 我会执行以下操作 SELECT FROM t1 WHERE MATCH t1 foo desc t2 bar desc AGAINS
  • 发布到 django Rest 框架

    我想使用以下方式发布到我的 Django 服务器post所以我可以添加一个todo物品 这是模型 class Todo models Model title models CharField max length 200 text mode
  • 如何使用 T-SQL 替换模式?

    我有规范化 POB 地址的代码 例如 其中包含的标准化之一是 set string replace string pobox pob 现在我想做类似的事情 我想找到任何直接跟在数字后面 中间没有空格 的 POB 并插入一个空格 我想找到模式
  • Chrome 无法播放 .mp4 文件

    我正在尝试让 HTML5 视频正常工作 我正在本地服务器上工作
  • ggplot 中的自定义形状(geom_point)

    Aim 我正在尝试改变形状geom point变成一个十字 所以不是 加 加 符号 而是 死亡 十字 Attempt 假设我有以下数据 library tidyverse df lt read table text x y 1 3 2 4
  • 如何以编程方式滚动到 UIWebView 的底部?

    我知道以前曾有人问过类似的问题 但似乎从未得到解答 我有一个 UIWebView 并通过字符串添加一些内容 我使用 UIWebView 是因为我动态地向其中添加一些图像 并且还使用其他 HTML 功能 此示例代码经过简化 NSString
  • pandas 在执行 groupby 后重置索引并保留选择性列

    我想采用 pandas 数据框 按列计算唯一元素并保留其中 2 列 但是我在 groupby 之后得到一个多索引数据框 我无法 1 展平 2 仅选择相关列 这是我的代码 import pandas as pd df pd DataFrame
  • 将虚拟地址与下一页边界对齐

    我遇到了以下算法 该算法将虚拟地址与紧邻的下一页边界对齐 VirtualAddr VirtualAddr PageSize 1 另外 给定字节长度 将长度 四舍五入 对齐到页边界上 len PageSize 1 len len PageSi
  • #1055 - SELECT 列表的表达式不在 GROUP BY 子句中并且包含非聚合列,这与 sql_mode=only_full_group_by 不兼容

    我的查询 select libelle credit initial disponible v sum montant as montant FROM fiche annee type where type id type annee id
  • jQuery 中的按键:在 TEXTAREA 内按 TAB 键(编辑现有文本时)

    我想在 TEXTAREA 中插入 TAB 字符 如下所示
  • 什么是基数以及它如何影响性能 (SQL Server)?

    我们有一个巨大的表 我需要在其中对单行进行更新 我不知道该行的主键 但我有一个在该表中唯一的 varchar 值 我还有该表中其他一些列的值 运行更新需要三分钟以上 我假设它进行了全表扫描 查看表上的索引 列上的索引的基数为零 页数为零 还
  • 强制 TextView 多行,不带 \n

    知道如何在视图内空间耗尽后强制文本视图转到新行吗 我想要发生的行为是 在不以编程方式找出大小并强制换行的情况下 我希望它自行发生 在此代码中 它强制按钮离开屏幕
  • 方法签名中的 params 关键字的真正含义是什么

    我正在浏览 Troelsen 的 Pro C 2010 并发现了有关 params 关键字方法修饰符的讨论 阅读文本 MSDN 和其他 tubez 来源 在我看来 从 params 获得的唯一东西就是能够将逗号分隔的值列表传递给方法 我编写
  • Android Studio 错误:无法在模拟器中启动 AVD

    错误 调整分区 e2fsck 大小失败 退出代码为 1 我已确保在设置 AVD 时完全按照此视频进行操作 每当我使用 x86 64 系统映像运行 AVD 时 我都会收到以下消息 无法在模拟器中启动 AVD 输出 创建文件系统 参数 大小 6
  • 如何使用私有构造函数从类创建对象?

    我有一个类游戏 它是我的主类 还有一个二类卡牌 Card 类的属性和构造函数是私有的 只有函数 init 是公共的 函数 init 检查值的合理性 如果一切正常 则构造函数将获取值并创建一个对象 现在我想在 Game 类中从 Card 类创
  • 在计算中使用布尔值以避免分支

    这是我想出的一些微观优化的好奇心 struct Timer bool running false int ticks 0 void step versionOne int mStepSize if running ticks mStepSi