从代码整洁的角度考虑,对于不同的值将调用相同参数的不同函数,我们通常可以通过建立从值到对应函数指针的哈希表,从而将if else消除。但实际可能使性能更低,以下是测试例子。

原因在于,if else分支预测不正确虽然可能使指令流水线几条指令执行错误,但通过哈希表的方式,增加了计算哈希值、查询哈希表以及通过函数指针调用的开销,从而可能使得调用过程慢了更多。

以下比较了四种方法的计算性能:

  1. if else

  2. unordered_map

  3. switch case

  4. function pointer array

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 10;
constexpr int COUNT = 10000000;
long long a = 0;
void targetFunc1(){
for(int i = 0; i < N; i++){
a++;
}
} void targetFunc2(){
for(int i = 0; i < N; i++){
a++;
}
} void targetFunc3(){
for(int i = 0; i < N; i++){
a++;
}
} void targetFunc4(){
for(int i = 0; i < N; i++){
a++;
}
} void targetFunc5(){
for(int i = 0; i < N; i++){
a++;
}
} void targetFunc6(){
for(int i = 0; i < N; i++){
a++;
}
} // 1. if else void test1(int a){
if(a == 1) targetFunc1();
if(a == 2) targetFunc2();
if(a == 3) targetFunc3();
if(a == 4) targetFunc4();
if(a == 5) targetFunc5();
if(a == 6) targetFunc6();
} std::unordered_map<int, void(*)()> funcs = {
{1, targetFunc1},
{2, targetFunc2},
{3, targetFunc3},
{4, targetFunc4},
{5, targetFunc5},
{6, targetFunc6}
}; // 2. unordered_map void test2(int a){
funcs[a]();
} // 3. switch case void test3(int a){
switch(a){
case 1:
targetFunc1();
break;
case 2:
targetFunc2();
break;
case 3:
targetFunc3();
break;
case 4:
targetFunc4();
break;
case 5:
targetFunc5();
break;
case 6:
targetFunc6();
break;
}
} // 4. function pointer array
void (*arr[6])(); void test4(int a){
arr[a-1]();
} void timeMeasure(void(*f)(int)){
auto begin = std::chrono::high_resolution_clock::now();
uint32_t iterations = 100;
for(uint32_t i = 0; i < iterations; ++i)
{
int index = rand() % 6 + 1;
f(index);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count();
std::cout << duration << "ns total, average : " << duration / iterations << "ns." << std::endl;
} int main()
{ arr[0] = targetFunc1;
arr[1] = targetFunc2;
arr[2] = targetFunc3;
arr[3] = targetFunc4;
arr[4] = targetFunc5;
arr[5] = targetFunc6;
timeMeasure(test1);
std::cout<<a<<std::endl;
timeMeasure(test2);
std::cout<<a<<std::endl;
timeMeasure(test3);
std::cout<<a<<std::endl;
timeMeasure(test4);
std::cout<<a<<std::endl;
return 0;
}
16859ns total, average : 168ns.
1000
41576ns total, average : 415ns.
2000
13834ns total, average : 138ns.
3000
14368ns total, average : 143ns.
4000

结论:

  1. switch case和函数指针数组查表比if else实现的性能较高

  2. unordered_map查表导致性能降低。

C++ 性能反向优化——用哈希表unordered_map消除if else导致性能降低。的更多相关文章

  1. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  2. 【算法】哈希表的诞生(Java)

    参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>       ...

  3. 性能调优7:多表连接 - join

    在产品环境中,往往存在着大量的表连接情景,不管是inner join.outer join.cross join和full join(逻辑连接符号),在内部都会转化为物理连接(Physical Joi ...

  4. 源码:Java集合源码之:哈希表(二)

    要想知道一个元素是否在数组或链表中,只能从前向后挨个对比,无论是数组还是链表,其对数据的查询表现都比较无力.在的二叉排序树中,还会将数据排序以进行二分查找,将时间复杂度从O(n)降低到O(lg n). ...

  5. 理解Golang哈希表Map的元素

    目录 概述 哈希函数 冲突解决 初始化 结构体 字面量 运行时 操作 访问 写入 扩容 删除 总结 在上一节中我们介绍了 数组和切片的实现原理,这一节会介绍 Golang 中的另一个集合元素 - 哈希 ...

  6. 6.MySQL优化---高级进阶之表的设计及优化

    转自互联网整理. 优化之路高级进阶——表的设计及优化 优化①:创建规范化表,消除数据冗余 数据库范式是确保数据库结构合理,满足各种查询需要.避免数据库操作异常的数据库设计方式.满足范式要求的表,称为规 ...

  7. STL的容器哈希表

    C++ STL中,哈希表对应的容器是 unordered_map(since C++ 11).根据 C++ 11 标准的推荐,用 unordered_map 代替 hash_map. 与Map的区别 ...

  8. mysql数据库性能优化(包括SQL,表结构,索引,缓存)

    优化目标减少 IO 次数IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作所占用的,减少 IO 次数是 SQL 优化中需要第一优先考虑,当 ...

  9. MySQL 性能优化系列之一 单表预处理

    MySQL 性能优化系列之一 单表预处理 背景介绍 我们经常在写多表关联的SQL时,会想到 left jion(左关联),right jion(右关联),inner jion(内关联)等. 但是,当表 ...

  10. MySQL性能优化(五):分表

    原文:MySQL性能优化(五):分表 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/vbi ...

随机推荐

  1. .NET6 API 部署标准流程

    一.安装dotnet环境 #第一步:将 Microsoft 包签名密钥添加到受信任密钥列表,并添加 Microsoft 包存储库 sudo rpm -Uvh https://packages.micr ...

  2. PowerBuilder现代编程方法X01:PowerPlume的X模式

    临渊羡鱼,不如退而结网. PB现代编程方法X01:PowerPlume的X模式 前言 PowerPlume是PowerBuilder深度创新的扩展开发框架(免费商用). 它不是一个大而全的类库(取决于 ...

  3. 使用 Node.js 和 Express 构建基本的 Web API

    使用 Node.js 和 Express 构建 Web API Web API Node.js 中的 http 模块 创建 Express 框架 Web 应用程序 Express 框架 Express ...

  4. ElementUI Dialog 结合Vue实现对话框body“二分”布局

    Dialog 结合Vue实现对话框body"二分"布局 需求描述 如下图, 把对话框body内容部分,分成上下两部分,其中上部分高度根据窗口大小动态调整,如果内容过多,则出现滚动条 ...

  5. k8s(3) 集群运行

    Master下面执行 mkdir -p $HOME/.kube 执行的脚本,需要读取的配置文件 cp -i /etc/kubernetes/admin.conf $HOME/.kube/config ...

  6. linux中whereis、which、find、locate的区别

    linux中whereis.which.find.locate的区别 1. find       fan路名含 find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件. find的使用实例 ...

  7. 【Java】【常用类】Calendar 日历类

    Calendar 日历类,我居然念错发音,来,好好看下音标  ['kælɪndə]  卡琳达 public class DateTest { public static void main(Strin ...

  8. 【RabbitMQ】01 简单模式

    参考自腾讯云的实验课堂,减少部署时间的折腾 1.创建Erlang.Repo库 curl -s https://packagecloud.io/install/repositories/rabbitmq ...

  9. 动手煮面的一次经历,加餐DIY

    去年和今年一直是有疫情的,也是如此像我这种在学校呆着的不是在家窝着就是在学校窝着,尤其是到了假期的时候,如果晚回家几天那食堂的伙食就会变得十分的恐怖. 面对这样的伙食也是十分的无奈,虽然也是十分理解当 ...

  10. 【转载】 DQN玩Atari游戏安装atari环境bug指南

    版权声明:本文为CSDN博主「好程序不脱发」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/ningmengzh ...