当前位置: 首页 > wzjs >正文

网站机房建设成本免费推广app

网站机房建设成本,免费推广app,小吃网站怎么做,南宁网站制作网络公司switch-case的性能优化策略 背景汇编分析的必要性快速生成汇编代码 switch-case 基本语法结构***代数归纳优化*** 编译器的三种汇编生成策略跳表(jump table)特点什么是跳表案例及说明 二分查找特点案例 顺序判断(if-else 风格)特…

switch-case的性能优化策略

  • 背景
    • 汇编分析的必要性
    • 快速生成汇编代码
  • switch-case 基本语法结构
    • ==***代数归纳优化***==
  • 编译器的三种汇编生成策略
    • 跳表(jump table)
      • 特点
      • 什么是跳表
      • 案例及说明
    • 二分查找
      • 特点
      • 案例
    • 顺序判断(if-else 风格)
      • 特点
      • 案例
  • 总结
    • 建议

【起源】
听到有人讨论if-else的执行效率,当分支较多时,if-else 往往需要逐个判断条件,最坏情况可能导致线性(O(N))的比较开销。switch-case 同样是一种控制流语句。有人说它的执行效率比 if-else 更高,但这是真的吗?为什么有这样的说法?不清楚。 switch 是如何确定跳转目标的?不清楚。
if倒是很好猜,通过一个一个条件判断进行比较,那switch-case呢?

在 C++ 开发中,switch-case 是常见的控制流语句之一。虽然它看似简单,但背后编译器的优化方式却多种多样,对程序性能有显著影响。本文从性能优化的角度出发,深入剖析 switch-case 的三种主要实现方式:线性比较(if-else 风格)、跳表(jump table) 和 二分查找(binary search tree),并提供示例与编译器生成汇编的分析。

【碎碎念】
所以switch-case的实现方式并不是只有一种,编译器会根据不同场景生成不同的汇编代码,那这几种汇编生成方式原理是什么?会有什么效率差异?

背景

汇编分析的必要性

同一段高级语言代码,在不同编译器、目标平台或优化等级下,可能会生成截然不同的汇编代码。
由于汇编语言是对机器语言的直接映射,仅比其抽象一层,因此能够清晰地展现程序在指令层级的真实执行方式。
通过分析汇编代码,我们可以在一定程度上评估程序的执行效率,定位性能瓶颈。

快速生成汇编代码

  • godbolt可以用于快速生成汇编代码

switch-case 基本语法结构

// 代码示例
int switch_dense(int x) {switch (x) {case 0: return x + 100;case 1: return x + 11;case 2: return x + 1002;case 3: return x + 1053;case 4: return x + 104;case 5: return x + 105;case 6: return x + 106;case 7: return x + 107;case 8: return x + 108;case 9: return x + 109;default: return -1;}
}

这是经典的switch-case写法,从源码层面写的都是switch-case,但是编译器会根据 case 值的数量和分布特征选择不同的优化策略,从而生成不同的汇编语言。

代数归纳优化

这里每一个case中的计算内容会尽量写的没有规律,不然编译器可能会有代数归纳优化,从而掩盖跳表/二分查找优化。
这里简单说一下代数归纳优化

int switch_dense(int x) {switch (x) {case 0: return 100;case 1: return 200;case 2: return 300;}return -1;
}

上述代码在汇编层面会直接优化为:

switch_dense2(int):cmp     edi, 2ja      .L13add     edi, 1imul    eax, edi, 100ret
.L13:mov     eax, -1ret

简单描述就是

若输入大于2,直接跳转到.L13,返回-1
若不是,则返回(x+1)*100

多个返回值因为过于规律直接被优化成了公式。

编译器的三种汇编生成策略

跳表(jump table)

特点

当 case 数值连续或接近连续的情况,也就是case数值为稠密的情况,通常超过 4~5 个连续 case,在设置优化级别大于-O2时GCC/Clang 会倾向于生成跳表。

每一个编译器都有自己的稠密标准判断,这里详细说明一下怎么计算,假设源码中case是顺序的

switch(x)
{case A: xxx
...................case B: xxx
}

A~B之间的case数量为n,n/(B-A + 1)就可以知道case的稠密程度,这个稠密程序会让编译器决定是否选择跳表,太稀疏的话就不会。

什么是跳表

  • 即函数指针数组,通过索引直接跳转。
  • 时间复杂度为 O(1),但空间占用较大(尤其稀疏时浪费空间)。

案例及说明

源代码

int switch_dense(int x) {switch (x) {case 0: return x + 100;case 1: return x + 11;case 2: return x + 1002;case 3: return x + 1053;case 4: return x + 104;case 5: return x + 105;default: return -1;}
}

使用godbolt生成的汇编代码,可以看到使用跳表之后非常快,当知道x的值是多少之后,直接根据偏移跳转到了对于的执行电,避免了条件判断,能达到O(1)的速度。

switch_dense(int):cmp     edi, 5                ; 判断参数 x 是否 > 5(case 最大值)ja      .L13                  ; 如果 x > 5,跳转到 default 分支; 否则说明 0 <= x <= 5,可以使用跳表快速定位mov     edi, edi             ; 加载跳表地址 + 偏移:eax = *(CSWTCH.7 + 4 * x)mov     eax, DWORD PTR CSWTCH.7[0+rdi*4]ret                          ; 返回 eax 中的结果.L13:mov     eax, -1              ; default: 返回 -1ret................................
................................
; 跳表数据区,一般位于 .rodata 或 .text 中
CSWTCH.7:.long   100   ; case 0: return 0 + 100 = 100.long   12    ; case 1: return 1 + 11 = 12.long   1004  ; case 2: return 2 + 1002 = 1004.long   1056  ; case 3: return 3 + 1053 = 1056.long   108   ; case 4: return 4 + 104 = 108.long   110   ; case 5: return 5 + 105 = 110

如果不是这种紧凑的,而是中间有空位的,例如把case 4的情况去掉,那么一样会生成跳表,只不过在对于的x == 4对于的位置直接返回-1

CSWTCH.7:.long   100.long   12.long   1004.long   1056.long   -1 ; x == 4.long   110

二分查找

特点

  • 编译器生成按值划分的比较树,类似手写二分。
  • 时间复杂度 O(log n),空间开销小于跳表,运行速度快于线性判断。

案例

int baz(int x) {switch (x) {case 1: return 10;case 5: return 50;case 10: return 100;case 20: return 200;case 40: return 400;case 80: return 800;default: return -1;}
}
baz(int):cmp     edi, 20je      .L17jg      .L16mov     eax, 50cmp     edi, 5je      .L14mov     eax, 100cmp     edi, 10je      .L14cmp     edi, 1mov     edx, 10mov     eax, -1cmove   eax, edxret
.L16:mov     eax, 400cmp     edi, 40je      .L14cmp     edi, 80mov     edx, 800mov     eax, -1cmove   eax, edxret
.L17:mov     eax, 200
.L14:ret

顺序判断(if-else 风格)

特点

  • 按顺序生成多个 cmp + je/jne 语句。
  • 时间复杂度为 O(n)。

案例

不开优化一般就是顺序判断

int switch_func(int x) {switch (x) {case 0: return x + 100;case 1: return x + 11;case 2: return x + 1002;default: return -1;}
}

汇编代码,就是一个一个顺序比较下去

switch_func(int):push    rbpmov     rbp, rspmov     DWORD PTR [rbp-4], edicmp     DWORD PTR [rbp-4], 2je      .L2cmp     DWORD PTR [rbp-4], 2jg      .L3cmp     DWORD PTR [rbp-4], 0je      .L4cmp     DWORD PTR [rbp-4], 1je      .L5jmp     .L3
.L4:mov     eax, DWORD PTR [rbp-4]add     eax, 100jmp     .L6
.L5:mov     eax, DWORD PTR [rbp-4]add     eax, 11jmp     .L6
.L2:mov     eax, DWORD PTR [rbp-4]add     eax, 1002jmp     .L6
.L3:mov     eax, -1
.L6:pop     rbpret

总结

开优化,混着用
一般编译器不会局限于一种方式,在一个switch中有可能会几种方式混着用,不过一般只要开了优化,最差也是二分查找,如果有一段case比较密集,还会跳表和二分法混着用。

建议

  • 尽量使用连续且小范围的 case 值。这样编译器倾向于使用跳转表(jump table)。
  • 开启编译器优化选项
http://www.dtcms.com/wzjs/415514.html

相关文章:

  • 用户体验设计流程网络公司优化关键词
  • 平台网站做代理商网站建设方案书 模板
  • 2015做外贸网站好做吗seo排名怎么样
  • 黄骅做网站_黄骅昊信科技|黄骅网站|黄骅网站开发|黄骅微信|黄骅有没有免费的广告平台
  • 没有货源可以开网店吗扬州整站seo
  • 偷拍网站做免费注册个人网站
  • 公司网站设计与制互联网推广项目
  • ubuntu下做网站seo扣费系统
  • 天津建设工程信息网专家灯塔网站seo
  • 高端网站建设哪家更专业建网站的软件有哪些
  • 哈尔滨网站建设吕新松微信管理系统登录
  • 网站建设及维护专业数据分析师要学什么
  • 如何做一名优秀的网站管理者小程序推广运营的公司
  • 昆明网站建设在河科技广州今日头条新闻
  • 苏州网站开发建设服务免费推广产品平台有哪些
  • 使用wordpress做网站电商数据统计网站
  • 张家口网站开发志鸿优化网
  • 做网站要服务器和什么软件如何销售自己产品方法有哪些
  • 网站排名优化课程seo快速排名是什么
  • 怎么做网站的需求旅行网站排名
  • wordpress不小心改了网站地址怎么做好市场宣传和推广
  • 网站数据分析的重要性广州搜索排名优化
  • 环境艺术设计最好的大学如何优化百度seo排名
  • 网站前台与后台建设的先后次序推广运营怎么做
  • 手机网站建设软件百度竞价被换着ip点击
  • 易语言可以做api网站对接吗百度搜索网页
  • 公司做网站一般多少钱运营软文写作服务
  • 建设景区网站的目的如何提高百度关键词排名
  • 网站不备案行吗百度seo软件曝光行者seo
  • 网站怎么做英语和中文的友情视频