从代码整洁的角度考虑,对于不同的值将调用相同参数的不同函数,我们通常可以通过建立从值到对应函数指针的哈希表,从而将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. 怒肝半月!Python 学习路线+资源大汇总

    Python 学习路线 by 鱼皮. 原创不易,请勿抄袭,违者必究! 大家好,我是鱼皮,肝了十天左右的 Python 学习路线终于来了~ 和之前一样,在看路线前,建议大家先通过以下视频了解几个问题: ...

  2. django 设置外键的时候,related_name的值大写还是小写,规则怎样

    django 设置外键的时候,related_name的值大写还是小写,规则怎样 在Django中,related_name参数用于定义反向关系的名称,即通过外键字段反向查询关联模型的对象.relat ...

  3. Python threading实现多线程 提高篇 线程同步,以及各种锁

    本文主要讲多线程的线程之间的资源共享怎么保持同步. 多线程基础篇见,Python threading实现多线程 基础篇 Python的多线程,只有用于I/O密集型程序时效率才会有明显的提高,如文件/输 ...

  4. 修改PE文件来实现管理员权限

    在Windows我们常用的方法就是给应用添加app.manifest清单文件,然后生成的Exe就会具有管理员权限. 近期我在使用Wix制作Exe安装包时,发现此方法不通,我在github上和Stack ...

  5. Android低功耗子系统的投票机制以及触发进入系统休眠的过程

    从kernel角度看,系统是否进入休眠应该由内核来控制,因此Linux引入了 wakeup source以及autosleep机制 关于wakeup source的介绍,请参考: Wakeup Sou ...

  6. 新年切红包-scratch小游戏

    程序说明: <新年切红包>是一款Scratch制作的小游戏,灵感来源于流行的切水果游戏.在这个游戏中,玩家需要用鼠标切割屏幕上不断飞出的红包,切割到红包将获得金币奖励,而切割到爆竹则会导致 ...

  7. 【Flutter】基础环境搭建

    一.下载 安装 配置 Android Studio 官网下载地址: https://developer.android.google.cn/studio?hl=zh-cn SDK下载,代理配置问题: ...

  8. 【H5】05 高阶文字排版

    摘自: https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formattin ...

  9. 【Mybatis-Plus】03 SpringBoot整合

    创建SpringBoot工程: 选择辅助三件套: 再导入MP相关依赖坐标: <!-- jdbc --> <dependency> <groupId>mysql< ...

  10. A* 算法、PathFinding问题中的 allow diagonal 和 don't cross corners,以及 .map文件格式(续)

    前文: A* 算法.PathFinding问题中的 allow diagonal 和 don't cross corners,以及 .map文件格式 上篇讲了些关于地图文件 .map 的介绍,本文主要 ...