0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

编译器如何对代码进行优化(下)

jf_78858299 来源:看雪william hill官网 彼岸风 作者:看雪william hill官网 彼岸风 2023-02-01 16:25 次阅读

变量乘常量

  • 常量为2的幂

乘法将会被替换为执行周期更短的移位指令。

int fun(int n) {
    return n * 16;
}
// mov eax, n
// shl eax, 4
  • 常量为非2的幂

因为 thumb 和 x86 指令集的差异,安卓平台上处理的更好一些。

我并不推荐你把自己当成编译器,看到算式想着怎么转成汇编,而是推荐记下这种算法,看到计算过程知道怎么转成原式,当然也不追求100%还原,逻辑一致即可。

编译器会对非2的幂进行拆解,例如:

  • n * 15 = n * 16 - n = n << 4 - n
  • n * 12 = n * 3 * 4 = (n << 1 + n) << 2
int value = n * 15;
// rsb.w r0, r1, r1, lsl #4

int value = n * 12;
// add.w r0, r1, r1, lsl #1

当然 windows 平台也不是一无是处,某些乘法会通过 lea 将两条指令合并成一条。

  • n * 4 + 5 = lea edx, [ecx * 4 + 5]
printf("%d", n * 4 + 5);
// mov ecx, n
// lea edx, [ecx * 4 + 5]
// push edx

至于值为不可拆分的素数,就改用 mul 指令。

变量乘变量

这一步没有什么优化空间,因为都是未知的,只能老老实实用 mul 指令。

int fun(int n, int m) {
    return n * m;
}
// mov eax, n
// mov ecx, m
// imul ecx

除法

在看下面内容之前,不妨再问问自己,真的了解除法吗?除法的本质是什么?

ok,现在是复习时间,简单总结一下以下两个问题。

  • 符号问题
    1. 两个无符号整数相除,结果依然是无符号
    2. 两个有符号整数相除,结果依然是有符号
    3. 混除,参数全被当成无符号计算,结果是无符号
  • 取整问题
    1. 向下取整 —— floor 函数 存在误差 => ( - a / b ) + ( a / b ) != - ( a / b ) - ( a / b )
    2. 向上取整 —— ceil 函数 存在误差 => ( - a / b ) != - ( a / b )
    3. 向零取整 —— 截断除法(Truncate),可以理解为放弃小数部分,只取整数部分,可以在任何情况保持恒等,大部分语言用的都是截断除法

除数为无符号数

  • 大数(负数)

在无符号中,负数的值是很大的,例如 -8 = 0xFFFFFFF8。

而除以这种大数,只能出现两种情况,1或 0,换个思路来想就可以写成这样:[被除数] >= [除数] ? 1 : 0

我们来看看 thumb 下是怎么优化的?

UINT value = (UINT)n / -8;
// cmn.w r0, #9    ; cmp r0, -9
// it hi
// movhi r1, #1    ; n > -9 ? 1 : 0

他这里做了一个小小的变形:[被除数] > [除数 - 1] ? 1 : 0,逻辑上仍然成立。

  • 2的幂

简单的移位

UINT value = (UINT)n / 4;
// lsrs r1, r0, #2
  • 非2的幂

接下来就要引入一个非常魔幻的设定,magic number。说来这个魔数,依稀记得早在几年前的知乎上看到过一篇文章,讲的是雷神之锤游戏引擎就使用了这么一个魔数,那时的cpu是非常低效的,而为了避免使用除法这种 cpu 周期偏长的指令,天才的程序员们想出了各种奇技淫巧,其中最为后人津津乐道的就是游戏中对平方根倒数的优化,将计算过程等价替换为加法和移位操作,损失少量的精度来换取绝对的性能。

我们这里的魔数稍有不同,它是用来优化除法的,而且逻辑上也相对容易理解一些,废话不多说,进入正题。

对于普通除法,我们可以得到以下的换算:(x => 被除数变量,c => 除数常量,M => 魔数)

假设用 M 代替 2^n / c 这个 Magic 变量,于是有:

也就是说,除法将会被转会成 (x * M) >> n 的逻辑进行运算,至于 M 和 n 值怎么来的,我们不关心,这是编译器根据除数算出来的最优值,会尽力保证偏差达到最小,我们要做的是认出魔数和移了多少位,然后根据 m = 2^n/c 公式求得原本的除数 c = 2^n/m

公式来源于《C++反汇编与逆向分析技术揭秘》,真的是非常非常的细,书中整个推导过程很完整,很建议各位去仔细研读一遍

以下代码为例:

printf("%u", (unsigned)argc / 3);
// mov eax, 0xAAAAAAAB   ; M
// mul [argc]            ; edx:eax = argc * M
// shr edx, 1            ; edx = argc * M >> 32 >> 1
// push edx

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4787

    浏览量

    68589
  • 编译器
    +关注

    关注

    1

    文章

    1634

    浏览量

    49128
  • Andorid
    +关注

    关注

    0

    文章

    7

    浏览量

    6989
收藏 人收藏

    评论

    相关推荐

    如何编写有利于编译器优化代码

    对于嵌入式系统,最终代码的体积和效率取决于由编译器生成的可执行代码,而非开发人员编写的源代码;但是源代码
    发表于 11-09 10:31 1410次阅读
    如何编写有利于<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>的<b class='flag-5'>代码</b>

    如何编写有利于编译器优化代码

    本篇文章将以国际知名编译器厂商IAR Systems的编译器为例,来解答开发人员在实际工作中常常遇到的问题,工程师朋友们可以在IAR编译器进行实践验证。
    发表于 08-01 09:43 486次阅读
    如何编写有利于<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>的<b class='flag-5'>代码</b>

    SIMD计算机的优化编译器设计

    利用处理的相关资源,提高编译器优化性能和增强代码可适应性是SIMD处理优化
    发表于 04-03 08:47 30次下载

    Keil C编译器编程规则和代码优化

    本内容介绍了Keil C编译器编程规则和代码优化,要实用好单片机就必须清楚它的内部结构组织结构,无论是在芯片的选择还是代码的编写
    发表于 04-20 17:37 315次下载
    Keil C<b class='flag-5'>编译器</b>编程规则和<b class='flag-5'>代码</b><b class='flag-5'>优化</b>

    编译器_keil的优化选项问题

    keil编译器优化选项针对ARM,对STM32编译的一些优化的问题
    发表于 02-25 14:18 3次下载

    C编译器及其优化

    本章将帮助读者在ARM处理上编写高效的C代码。本章涉及的一些技术不仅适用于ARM处理,也适用于其他RISC处理。本章首先从ARM编译器
    发表于 10-17 17:22 2次下载

    编译器优化对函数的影响

    编译器如gcc,可以指定不同的优化参数,在某些条件,有些函数可能会被优化掉。
    的头像 发表于 06-22 14:58 2838次阅读
    <b class='flag-5'>编译器</b><b class='flag-5'>优化</b>对函数的影响

    如何编写有利于编译器优化代码

    对于嵌入式系统,最终代码的体积和效率取决于由编译器生成的可执行代码,而非开发人员编写的源代码;但是源代码
    的头像 发表于 03-29 15:58 1489次阅读
    如何编写有利于<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>的<b class='flag-5'>代码</b>

    编译器如何对代码进行优化(上)

    在学习 Andorid 逆向的过程中,发现无论是哪种编译器,生成哪个平台的代码,其优化思路在本质上如出一辙,在 Windwos 平台所使用的技巧,在安卓平台仍然适用,不外乎乘法除法计算的优化
    的头像 发表于 02-01 16:25 910次阅读

    编译器优化选项

    这一点,需要了解编译器的能力和限制;第三,要了解硬件的运行方式,针对硬件特性进行优化。本文着重展开第二点和第三点。 简单认识编译器 要写出高性能的
    的头像 发表于 11-24 15:37 905次阅读
    <b class='flag-5'>编译器</b>的<b class='flag-5'>优化</b>选项

    Keil编译器优化方法

    我们都知道,代码是可以通过编译器优化的,有的时候,为了提高运行速度或者减少代码尺寸,会开启优化选项。
    的头像 发表于 10-23 16:35 544次阅读
    Keil<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>方法

    Triton编译器与其他编译器的比较

    的GPU编程框架,使开发者能够编写出接近手工优化的高性能GPU内核。 其他编译器 (如GCC、Clang、MSVC等): 定位:通用编译器,支持多种编程语言,广泛应用于各种软件开发场景。 目标:提供稳定、高效的
    的头像 发表于 12-24 17:25 297次阅读

    Triton编译器优化技巧

    在现代计算环境中,编译器的性能对于软件的运行效率至关重要。Triton 编译器作为一个先进的编译器框架,提供了一系列的优化技术,以确保生成的代码
    的头像 发表于 12-25 09:09 158次阅读

    Triton编译器如何提升编程效率

    开发者能够更快地开发出更高效的软件。 1. 代码优化 1.1 编译优化 Triton 编译器编译
    的头像 发表于 12-25 09:12 162次阅读

    Triton编译器与GPU编程的结合应用

    Triton编译器简介 Triton编译器是一种针对并行计算优化编译器,它能够自动将高级语言代码转换为针对特定硬件
    的头像 发表于 12-25 09:13 155次阅读