ARM下高效C编程

2023-11-09

 

通过一定的风格来编写 C 程序,可以帮助 C 编译器生成执行速度更快的 ARM 代码。下面就是一些与性能相关的关键点:
       1 、对局部变量、函数参数和返回值要使用 signed unsigned int 类型。这样可以避免类型转换,而且可高效地使用 ARM 32 位数据操作指令。
       2 、最高效的循环体形式是减计数到零( counts down to zero )的 do-while 循环。
       3 、展开重要的循环来减少循环的开销。
       4 、不要依赖编译器来优化掉重复的存储器访问。指针别名会阻止编译器的这种优化。
       5 、尽可能把函数参数的个数限制在 4 个以内。如果函数参数都存放在寄存器内,那么函数调用就会快得多。
       6 、按元素尺寸从小到大排列的方法来安排结构体,特别是在 thumb 模式下编译。
       7 、不要使用位域,可以用掩码和逻辑操作来替代。
       8 、避免除法,可以用倒数的乘法来替代。
       9 、避免边界不对齐的数据。如果数据有可能边界不对齐,那么就要使用 char * 指针类型来访问。
       10 、在 C 编译器中使用内嵌汇编可以利用到 C 编译器本来不支持的指令或优化。
      
一、        数据类型使用上的优化
1 、局部变量
一个 char 类型的数据比 int 类型的数据占用更小的寄存器空间或者更小的 ARM 堆栈空间。这两种设想对于 ARM 来说,都是错误的。所有的 ARM 寄存器都是 32 位的,所有的堆栈入口至少是 32 位的。当我们执行 i++ ,要利用当 i=255 后, i++=0 这个条件时,可以把它定义为 char 类型。
       2 、函数参数
              尽管宽和窄的函数调用规则各有其优点,但 char short 类型的函数参数和返回值都会产生额外的开销,导致性能的下降,并增加了代码尺寸。所以,即使是传输一个 8 位的数据,函数参数和返回值使用 int 类型也会更有效。      
       3 、总结
              1 )对于存放在寄存器中的局部变量,除了 8 位或 16 位的算术模运算外,尽量不要使用 char short 类型,而要使用有符号或无符号 int 类型。除法运算时使用无符号数执行速度更快。
              2 )对于存放在主存储器中的数组和全局变量,在满足数据大小的前提下,应尽可能使用小尺寸的数据类型,这样可以节省存储空间。 ARMv4 体系结构可以有效地装载和存储所有宽度的数据,并可以使用递增数组指针来有效地访问数组。对于 short 类型数组,要避免使用数组基地址的偏移量,因为 LDRH 指令不支持偏移寻址。
              3 )通过读取数组或全局变量并赋给不同类型的局部变量时,或者把局部变量写入不同类型的数组或者全局变量时,要进行显式数据类型转换。这种转换使编译器可以明确、快速地处理,把存储器中数据宽度比较窄的数据类型扩展,并赋给寄存器中较宽的类型。
              4 )由于隐式或者显式的数据类型转换通常会有额外的指令周期开销,所以在表达式中应尽量避免使用。 Load store 指令一般不会产生额外的转换开销,因为 load store 指令是自动完成数据类型转换的。
              5 )对于函数参数和返回值应尽量避免使用 char short 类型。即使参数范围比较小,也应该使用 int 类型,以防止编译器做不必要的类型转换。
二、 C 循环结构
ARM 上,一个循环其实只要 2 条指令就足够了:
一条减法指令,进行循环减法计数,同时设置结果的条件标志;
一条条件分支指令。
这里的关键是,循环的终止条件应为减计数到零,而不是计数增加到某个特定的限制值。由于减计数结构已存储在条件标志里,与零比较的指令就可以省略了。由于不用 i 作为数组的下标索引,采用减计数就没有任何问题了。
总而言之,无论对于有符号的循环计数值,都应使用 i =0 作为循环的结束条件。 对有符号数 i ,这比使用条件 i>0 少了一条指令。
总结:
1   使用减计数到零的循环结构,这样编译器就不需要分配一个寄存器来保存循环终止值,而且与 0 比较的指令也可以省略。
2   使用无符号的循环计数值,循环继续的条件为 i!=0 而不是 i>0 ,这样可以保证循环开销只有两条指令。
3   如果事先知道循环体至少会执行一次,那么使用 do-while 循环要比 for 循环要好,这样可以使编译器省去检查循环计数值是否为零的步骤。
4   展开重要的循环体可降低循环开销,但不要过度展开,如果循环的开销对整个程序来说占的比例很小,那么循环展开反而会增加代码量并降低 cache 的性能。
5   尽量使数组的大小是 4 8 的倍数,这样可以容易的以 2 4 8 次等多种选择展开循环,而不需要担心剩余数组元素的问题。
三、          寄存器分配
高效的寄存器分配
应该尽量限制函数内部循环所用局部变量的数目,最多不超过 12 个,这样,编译器就可以把这些变量都分配给 ARM 寄存器。
四、          函数调用
4 寄存器规则
带有 4 个或者更少参数的函数,要比多于 4 个参数的函数执行效率高得多。对带有少于 4 个参数的函数来说,编译器可以用寄存器传递所有的参数;而对于多于 4 个参数的函数,函数调用者和被调用者必须通过访问堆栈来传递一些参数。
如果函数体积很小,只用到很少的寄存器,那么还有一些其他的方法来减少函数调用的开销。可以把调用函数和被调用函数放在同一个 C 文件中,这样编译器就知道了被调用函数生成的代码,并以此对调用函数进行一些优化。
总结:
1   尽量限制函数的参数,不要超过 4 个,这样函数调用的效率会更高。也可以将几个相关的参数组织在一个结构体中,用传递结构体指针来代替多个参数。
2   把比较小的被调用函数和调用函数放在同一个源文件中,并且要先定义,后调用,编译器就可以优化函数调用或者内联较小的函数。
3   对性能影响较大的重要函数可使用关键字 _inline 进行内联。
五、          指针别名
定义:当 2 个指针指向同一个地址对象时,这 2 个指针被称作该对象的别名( alias )。如果对其中一个指针进行写入,就会影响从另一个指针的读出。在一个函数中,编译器通常不知道哪一个指针是别名,哪一个不是;或哪一个指针有别名,哪一个没有。
避免指针别名:
1   不要依赖编译器来消除包含存储器访问的公共子表达式,而应建立一个新的局部变量来保存这个表达式的值,这样可以保证只对这个表达式求一次值;
2   避免使用局部变量的地址,否则对这个变量的访问效率会比较低。
六、        结构体安排
ARM 上使用结构体有 2 个问题需要考虑:结构体地址边界对齐和结构体总的大小。
获得高效结构体的原则:
1   把所有 8 位大小的元素安排在结构体的前面;
2   以此安排 16 位、 32 位和 64 位的元素;
3   把所有数组和比较大的元素安排在结构体最后。
4   对于一条指令,如果结构体太大而不能访问所有的元素,那么把元素组织到一个子结构体中。编译器可以维持单独的子结构体的指针。
小结:
结构体元素要按照元素的大小来排列,以最小的元素放在开始,最大的元素安排在最后;避免使用很大的结构体,可以用层次化的小结构体来代替;为了提高可移植性,人工对 API 的结构体增加填充位,这样,结构体的安排将不会依赖与编译器;在 API 的结构体中要谨慎使用枚举类型。一个枚举类型的大小是编译器相关的。
七、        位域
注意事项:
1   应避免使用位域,而使用 #define 或者 enum 来定义屏蔽位;
2   使用整型逻辑运算 AND OR 异或 操作和屏蔽对位域进行测试、取反和设置操作。这些操作编译效率高,还可以同时对多个位域进行测试、取反和设置。
八、            边界不对齐数据和字节排列方式(大 / 小端)
边界不对齐数据和字节排列方式这 2 个问题,可使内存访问和移植问题复杂化。须考虑数组指针是否边界对齐, ARM 配置是大端( big-endian ),还是小端( little-endian )的存储器系统。
总结:
1   尽量避免使用边界不对齐的数据;
2   使用类型 char * 可指向任意字节边界的数据。 通过读字节来访问数据,使用逻辑操作来组合数据,这样代码就不会依赖于边界是否对齐或者 ARM 的字节排列方式的配置;
3   为了快速访问边界不对齐的结构体,可以根据指针边界和处理器的字节排序方式写出不同的程序变体。
九、 除法
ARM 硬件上不支持除法指令,当代码中出现除法运算时, ARM 编译器会调用 C 库函数(有符号的除法调用 _rt_sdiv ,无符号的调用 _rt_udiv ),来实现除法操作。有许多不同类型的除法程序来适应不同的除数和被除数。
总结:
1   尽可能避免使用除法。对环形缓冲区的处理可以不用除法。
2   如果不能避免除法运算,那么尽可能考虑使用除法程序同时产生商 n/d 和余数 n%d 的好处。
3   对于重复对同一除数 d 的除法,预先计算好 s=(2k-1)/d 。可用乘以 s 2k 位乘法来代替除以 d k 位无符号整数除法。
4 )使用 2 的整数次幂作除数。当 2 的整数次幂做除数时,编译器会自动将除法运算转换成移位运算。所以在编写程序算法时,尽量使用 2 的整数次幂做除数。
5 )求余运算。可以将一些典型的求余运算进行转换,以避免在程序中使用除法运算。如:
uint counter1(uint count)
{
        return (++count%60);
}
转换成:
uint counter2(uint count)
{
        if (++count >=60)
        count=0;
        return (count);
}
十、        浮点运算
大多数 ARM 处理器硬件上并不支持浮点运算。这样在一个对价格敏感的嵌入式应用系统中,可节省空间和降低功耗。除了硬件向量浮点累加器 VFP ARM7500FE 上的浮点累加器 FPA 外, C 编译器必须在软件上提供浮点支持。
十一、内联函数和内嵌汇编
高效地调用函数,使用内联函数可以完全去除函数调用的开销,另外许多编译器允许在 C 源程序中使用内嵌汇编。使用包含汇编的内嵌函数,可以使编译器支持通常不能有效使用的 ARM 指令和优化方法。
       内联函数和内嵌汇编最大的好处是,可以实现一些在 C 语言部分中通常难以完成的操作。使用内联函数要比使用 #define 宏定义更好,因为后者不检查函数参数和返回值的类型。
 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ARM下高效C编程 的相关文章

  • 使用 gcc 在 Linux 上运行线程构建块 (Intel TBB)

    我正在尝试为线程构建块构建一些测试 不幸的是 我无法配置 tbb 库 链接器找不到库 tbb 我尝试在 bin 目录中运行脚本 但这没有帮助 我什至尝试将库文件移动到 usr local lib 但这又失败了 任何的意见都将会有帮助 确定您
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • C++ 继承的内存布局

    如果我有两个类 一个类继承另一个类 并且子类仅包含函数 那么这两个类的内存布局是否相同 e g class Base int a b c class Derived public Base only functions 我读过编译器无法对数
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置

随机推荐

  • Qt的QMessageBox消息弹窗

    Qt的消息弹窗QMessageBox Qt的消息弹窗QMessageBox 1 说明 2 6种消息框 3 QMessageBox StandardButton 枚举类型值 3 1 information消息对话框 3 1 1 informa
  • RabbitMQ 消息有效期问题

    目录 一 默认情况 二 TTL Time To Live I TTL 的简介 II 单条消息过期 III 队列消息过期 IV 特殊情况 三 死信队列以及死信交换机 I 死信交换机 II 死信队列 III 具体操作 一 默认情况 在默认情况下
  • html 模板

    模板王 10000 免费网页模板 网站模板下载大全 mobanwang com http www mobanwang com
  • IEEE Transactions模板中参考文献作者缩写、期刊名缩写

    IEEE Transactions模板中参考文献作者缩写 期刊名缩写 本文章记录如何在IEEE Transactions的模板中 解决参考文献的作者缩写 期刊名字缩写的问题 目录 IEEE Transactions模板中参考文献作者缩写 期
  • python爬虫一:爬虫简介

    1 什么是爬虫 络爬 被称为 蜘蛛 络机器 就是模拟客户端发送 络请求 接收请求响应 种按照 定的规则 动地抓取互联 信息的程序 只要是浏览器能做的事情 原则上 爬 都能够做 可见即可爬 1 1爬虫有哪些用途 为其他数据提供数据源 像AI人
  • 数据挖掘的特点

    数据挖掘具有以下几个特点 1 基于大量数据 并非说小数据量上就不可以进行挖掘 实际上大多数数据挖掘的算法都可以在小数据量上运行并得到结果 但是 一方面过小的数据量完全可以通过人工分析来总结规律 另一方面来说 小数据量常常无法反映出真实世界中
  • kettle运行spoon.bat时找不到javaw文件

    我也遇到这问题了 分享一下解决方法吧以后没准还有人能用到 我机器的主要问题是环境变量JAVA HOME的值不对 应该写到jdk也就是C Program Files Java jdk1 7 0 25 并且 改完后要重启机器才行 这个很重要
  • DNS服务器的安装与配置

    一 DNS服务器的安装 步骤1 选择 开始 控制面板 添加或删除程序 添加 删除Windows组件 然后选取 网络服务 组件 再单击详细信息按钮 步骤2 选取 域名系统 DNS 组件后单击 确定 按钮 步骤3 回到前一个画面后 单击 下一步
  • vscode远程开发及公钥配置(告别密码登录)

    文章目录 vscode远程开发及公钥配置 简介 关于远程开发官网简介 关于SSH简介 环境 插件安装 配置服务器 找到配置文件 修改配置文件 连接服务器 配置密钥 简介 密钥生成 服务器上安装公钥 查看或配置打开密钥登录功能 服务器私钥复制
  • SSL/TLS 双向认证(一) -- SSL/TLS 工作原理

    本文部分参考 https www wosign com faq faq2016 0309 03 htm https www wosign com faq faq2016 0309 04 htm http blog csdn net hher
  • 四川计算机专业高职高考,四川职高计算机专业分数线

    类似问题答案 2016年贵州大学计算机类专业在四川录取分数线 学校 地 区 专业 年份 批次 类型 分数 贵州大学 四川 计算机类 2016 一批 理科 597 学校 地 区 专业 年份 批次 类型 分数 贵州大学 四川 计算机类 2016
  • 【华为OD统一考试B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • 分布式数据库核心原理 Zookeeper+Mysql

    原文 作者 1菩提行者1 笔者一直做java开发 由于技术演进做过大型微服务项目 微服务即将一个大的服务拆分成一个一个小的微服务 每个微服务自成生态 而在落地过程中紧紧只是应用层拆分 数据层往往用同一个库 有点形变神不变 当然将微服务与其对
  • JavaScript如何截取指定位置的字符串

    我们在日常开发中 经常需要对字符串进行删除截取增加的操作 我们这次说一下使用JavaScript截取指定位置的字符串 一 使用slice 截取 slice 方法可以通过指定的开始和结束位置 提取字符串的某个部分 并以新的字符串返回被提取的部
  • MIPI介绍(CSI DSI接口)

    MIPI介绍 CSI DSI接口 MIPI介绍 CSI DSI接口 视频接口 2 MIPI Solution mipi接口 缘来是你远去是我的博客 CSDN博客 MIPI LVDS RGB HDMI等接口对比 mipi和lvds区别 芒果5
  • socket编程

    socket 可以看做用户进程与内核网络协议栈的编程接口 可以用于本机进程间 网络上不同主机进程间的通信 对等通信 是全双工的 socket 异构系统 所以需要统一字节序统一后的字节序为大端字节序 x86为小端字节序 字节序转换函数 可以看
  • vsCode插件安装之汉化和浏览器打开

    一 汉化的方法 点击最左面第五个图标 在搜索框里面输入Chinese 点击如图第一个内容 点击Install 安装 安装后 重启软件即可 二 浏览器打开html 文件方法 在安装插件窗口搜索Browser 点击如图内容 点击install安
  • SpringBoot(十)SpringBoot自定义starter

    一个月的时间 转眼已经到了我的SpringBoot系列的第十篇文章 还记得我的第二篇文章SpringBoot 二 starter介绍 springboot的starter heart荼毒的博客 CSDN博客 曾经介绍过starter sta
  • mmdetection训练自己的VOC数据集 label=self.cat2label 报错解决方案

    废话不多说 直接上报错的图 看了GitHub上的大佬的回答 报错的原因是self cat2label值不对 所以根据大佬的建议 我print了self cat2label值 发现果然不对 类还是VOC数据集的类 而不是我自己的类 我的类是
  • ARM下高效C编程

    通过一定的风格来编写 C 程序 可以帮助 C 编译器生成执行速度更快的 ARM 代码 下面就是一些与性能相关的关键点 1 对局部变量 函数参数和返回值要使用 signed 和 unsigned int 类型 这样可以避免类型转换 而且可高效