从代码整洁的角度考虑,对于不同的值将调用相同参数的不同函数,我们通常可以通过建立从值到对应函数指针的哈希表,从而将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. 使用Selenium爬取动态网页

    1.使用Selenium爬取动态网页 In [ ]: from selenium import webdriver driver = webdriver.Chrome() driver.get('ht ...

  2. Python 潮流周刊#60:Python 的包管理工具真是多啊(摘要)

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  3. JavaScript系列:JS实现复制粘贴文字以及图片

    目录 一. 基于 Clipboard API 复制文字(推荐) 基本概念 主要方法 使用限制 实际应用示例 二.基于 document.execCommand('copy') 缺陷 实际应用示例 说明 ...

  4. mysql 删除数据表报错 表删除时 Cannot delete or update a parent row: a foreign key constraint fails 异常处理

    mysql 删除数据表报错 表删除时 Cannot delete or update a parent row: a foreign key constraint fails 异常处理 MySQL报错 ...

  5. 利用FastAPI和OpenAI-Whisper打造高效的语音转录服务

    最近好久没有写博客了,浅浅记录下如何将OpenAI-Whisper做成Web服务吧 介绍 在这篇指导性博客中,我们将探讨如何在Python中结合使用FastAPI和OpenAI-Whisper.Ope ...

  6. [oeasy]教您玩转python - 0004 - 万行代码之梦

    ​ 继续运行 回忆上次内容 上次从1行代码进化到了2行代码 yy p粘贴剪贴板中的内容 将剪贴板中的代码粘贴9999次 9999p 真的实现了万行代码梦 是真·圆梦 没有撒谎的那种 不过圆梦之后多少有 ...

  7. c++ 17 demo

    1 // Cpp.cpp : 此文件包含 "main" 函数.程序执行将在此处开始并结束. 2 // 3 4 #include <iostream> 5 #includ ...

  8. 面试题-python 什么是闭包(closure)?

    前言 前面学了装饰器,那么闭包和装饰器有什么区别呢?闭包传递的是变量,而装饰器传递的是函数对象,只是传的参数内容不一样,闭包的概念包含了装饰器,可以说装饰器是闭包的一种,它只是传递函数对象的闭包. 先 ...

  9. iOS开发基础142-广告归因

    IDFA IDFA是苹果为iOS设备提供的一个唯一标识符,专门用于广告跟踪和相关的营销用途.与之对应的,在Android平台的是谷歌广告ID(Google Advertising ID). IDFA的 ...

  10. U盘安装Ubuntu18.04系统

    U盘安装Ubuntu18.04系统 一.安装盘制作 根据您当前使用系统的不同,需要不同的工具制作 U 盘启动安装盘.目前主要有 Linux 系统和 Windows 系统两类. 1.Linux 系统 ( ...