当前位置:首页 > 编程开发

iOS 开发者应该知道的 ARM 结构

webgou16年前 (2010-09-24)编程开发195

我在写「NEON on iPhone 入门」 的时候,曾以为读者已经比较了解 iOS 设备的处理器知识。然而,看过网上的一些讨论,我才发现,原来这些知识并不普及,我的错。此外,我觉得了解这些东西对 iPhone 编程有益(不仅仅针对喜欢 NEON 的人),即便你用的是 Objective-C,虽然,不了解也无碍工作,但这些知识会让你成为一个更好的 iPhone 程序员。

基础

到目前为止,所有的 iOS 设备都使用 ARM 结构处 理器,它和台式机上的 x86 和 PowerPC 有些不同,然而绝对不是「特殊」或「小众」的产品。几乎所有的手机(不只是智能手机)都基于 ARM,例如几乎所有的 iPod,几乎所有的 MP3 播放器,PDA 和 Pocket PC 更不用说了。任天堂从 GBA 开始转入 ARM,它甚至还侵入图形计算器的地盘,出现在一些德仪和惠普的计算器中。如果你还想继续溯本逐源,那么牛顿用的也是 ARM(苹果是 ARM 的早期投资者)。而且上面只说了一些小玩意,还有无数的 ARM 处理器运行在嵌入式系统中。

ARM 处理器因为低功耗和小尺寸而闻名,它的性能在同等功耗的产品中也很出色。这种结构(至少在 iOS 平台)使用小端(Little-endian)排序,就像 x86。它和 MIPS、PowerPC 一样,属于 32 位 RISC 结构。请注意,模拟器并不运行 ARM 代码,软件会被编译成 x86 可以运行的指令。因此接下来的内容适用于目标设备,而非模拟器。

ARMv7,ARM11,Cortex A8 和 A4,天哪!

多年来,ARM 结构演化出几个不同的版本,每一版都增加了新指令,在提升的同时保持了后向兼容的能力。初代 iPhone 使用了 ARMv6 结构的处理器(ARM 第六版的简称),而最新的 iPhone 4 支持 ARMv7。所以,编译代码的时候,依目标版本的指令集不同,生成不同的指令。汇编程序也一样,代码中使用的指令必须兼容特定的版本。最后,生成机器码, 对应 ARMv6 或 ARMv7(或者 ARMv5 和 v4,不过 ARMv6 是 iOS 开发的底线,所以这两者就不用考虑了)。目标文件和可执行文件有标注自己对应的版本,可以通过运行 otool -vh foo.o 来查看。

不过呢,「初代 iPhone 4 搭载了 ARMv6 处理器」这种说法是错误的,因为 ARMv6 不是指特定的处理器,而是处理器可以运行的指令集。初代 iPhone 使用了 ARM 11 核心(确切说是 ARM1176JZF-S,不过这不重要,只要记得它是 ARM 11 家族的成员就行了),正如刚才提到的,这款处理器采用 ARMv6 指令集。之后的 iOS 设备仍采用 ARM11,直到 iPhone 3GS 发布,苹果开始尽数转向 Cortex A8 处理器核心(尽管尚不确定,但 iPhone 4 很可能用的就是 A8 )。这个核心采用了 ARMv7 指令集,或这么说,它支持 ARMv7。

我已经说过,不要在程序里植入设备判断代码,然后通过已知信息侦测设备所支持的 ARM 结构。这种代码极不可靠,而且运行在(软件完成后才发布的)新设备上会导致中断。所以请别这么做,否则我发誓,我会跑到你家里废了你。以上知识是为了让你 粗略了解,有些设备支持 ARMv7,有些设备支持 ARMv6。至于如何侦测,我马上会谈到。

不过,你可能会想「iPad 和 iPhone 4 用的是 A4,不是 Cortex A8 吧?」不然,A4 其实是一个完整的单片系统(SOC),其中不只有 Cortex A8 内核,还包括了图形硬件、音视频编码加速器和其他数字模块。单片系统和处理器是两个很不相同的概念,处理器在硅片上甚至不占主要空间。

如果不懂得如何利用,即使设备支持 ARMv7 也无济于事。当然应用新的指令集也没有问题,但如果总是这么做,早先的设备就无法运行你写的代码了,我猜,这也许不是你想要的结果。那么,应该如何侦测设 备所支持的结构呢?— 只有确定它是否支持 ARMv7 才能好好利用啊。答案是:没必要知道。相反,把代码编译两次,一次针对 ARMv6,另一次针对 ARMv7,接着把这两个可执行文件打包成一坨肥硕无比的二进制文件。好了,运行的时候,设备会自己决定打开哪一个更好。是的,Mach-O 不仅可以用来组合完全不同的 CPU 结构(例如 PowerPC 和 Intel),或者相同结构的 32 位和 64 位版本,它还可以对付同一种结构的 2 个变体,用 Mach-O 的术语来说,这叫 CPU 子类。从程序员的角度看,这么做的结果是:编译时决定一切。针对 ARMv6 编译的代码只运行在 ARMv6 设备上,同理,针对 ARMv7 编译的代码只运行在 ARMv7(或者更好)的设备上。

如果你读过了我写的 NEON 那帖,你也许会记得我推荐过一种在运行时(Runtime)中侦测和选择结构的方法。如果再去看,你会发现我已经把那部分移走了,现在,我不建议那么做, 因为虽然这的确有用,但不能确保(或者说,所需技巧太复杂而不能确保不出错)在将来的 ARMv8 处理器上能够稳定运行。文档中是否有相关 API 的状态不重要(不在 iOS 的手册页中),如果你想在 ARMv6 上运行又希望利用 ARM7v,就用我刚才讲过的办法。

补充一点:在 iOS 环境下,ARM 结构不一定能反映处理器的型号。例如,对应 ARMv6 的 iOS 代码需要浮点指令的支持(VFPv2,准确的说),对 ARMv6 而言,虽然这是可选项,不过自从第一代 iPhone 发布以来就已经存在。所以,如果在 iOS 开发(例如编译器 -arch 设置或一个可执行文件的 CPU 子类)中提到了 ARMv6,就表示需要硬件浮点的支持。这对 ARMv7 和 NEON 也一样:虽然 NEON 实际上是 ARMv7-A 配置的一个可选项,但是因为它出现在所有支持 ARMv7 的 iOS 设备中,所以,提到 iOS NEON 即部分提到 ARMv7。

条件执行

ARM 结构一个实用的功能是,大多数指令可以有条件地执行 — 如果条件不满足,则指令无效。这可以缩短过程,让区块(Blocks)部署地更为有效。通常的办法是,如果区块不符合条件则跳过,但是通过把判断指令植入块内,省去了该步骤。

如果这仅仅是编译器用来提高代码效率的手段,我就不会在这里提到它了。虽然,这的确是它的一个功用,但之所以提到是因为,在调试 (Debugging)时,它可能会令人吃惊。事实上,有时你会发现,调试器会进入状态为假的条件区块(if block,例如早期的错误回报),或者进入 if-else 的两个分支。这是因为,虽然代码尽数经过处理器,但是一部分没有实际执行,即条件执行。另外,如果你把断点置入这样的条件区块中,即使状态为假,它仍有可 能执行。

话虽如此,但是在我有限的测试中,编译器似乎拒绝在调试配置中生成条件执行指令。因此它应该只发生在调试优化后的代码的时候,不幸的是,有时候你没得选择,只能这么做。

Thumb

Thumb 指令集是 ARM 指令集的一个子集,经过压缩,因此指令只有 16bits(所有 ARM 指令的大小都是 32bits,它仍然是 32 位结构,只是占用的空间少了)这不是一个全然不同的结构,而应将其视作常见 ARM 指令和功能的缩写。它的优点,显然是大为缩小代码尺寸,节约内存和缓存,以及代码带宽。虽然更适用于内存紧张的微控制器型应用程序,但是在 iOS 设备中,它仍然有用处,也因为如此,Xcode 默认在 iOS 项目中打开这项功能。虽然代码尺寸因此减少很多,但是不可能达到 50%,因为有时候完成一个 ARM 指令需要对应的两个 Thumb 指令。ARM 和 Thumb 指令不能随意混合,处理器需要针对二者切换不同的模式,而这只能在调用或从函数返回时发生。

当目标平台是 ARMv6 的时候,编译 Thumb 指令面临着很大的权衡取舍。ARMv6 的 Thumb 代码可以访问的寄存器较少,缺乏条件指令,特别是,它不能使用浮点硬件,例如浮点加法、减法、乘法等等。使用浮点 Thumb 代码必须调用系统函数,没错,听起来就像速度很慢的感觉。基于这个原因,针对 ARMv6 时,我建议禁用 Thumb 模式,但倘若你执意如此,请确保先分析代码。如果某些部分速度很慢,至少先试着禁用那部分 Thumb(很容易,在 Xcode 中使用命令行参数, -mno-thumb)。请记住,浮点运算在 iOS 中非常普遍,因为 Quartz 和 Core Animation 使用浮点坐标系统。

当目标变成了 ARMv7 的时候,所有这些缺点就消失了:ARMv7 包含 Thumb-2,它是 Thumb 指令的扩展集,增加了条件执行和可以访问所有 ARM 寄存器以及硬件浮点与 NEON 的 32 位 Thumb 指令。用 Thumb-2 缩减代码的代价几乎没有,所以最好是开着(如果关掉了请重新打开)。在 Xcode 的条件生成选项中,对 ARMv7 打开,对 ARMv6 关闭。

你也许在网上听到人们说,代码需要「互通」(Interworking)才能使用 Thumb,除非你想写汇编代码,否则不必担心,因为 iOS 平台的所有代码都是互通的。当显示汇编的时候,Shark 可能难以判断函数是 ARM 还是 Thumb。如果你看到无效或无意义的指令,最好互相对调一下。

对齐

iOS 支持非对齐访问,然而比起对齐访问,它的速度更慢,建议不要使用。在某些特殊情况下(涉及加载/存储多个指令,如果你有兴趣的话),非对齐访问的速度可能比对齐访问慢上百倍,因为处理器无法处理,而且必须请求操作系统的协助(参考此文,这和 PowerPC 上导致非对齐双精度浮点数变得超慢是同一个现象)。所以,要小心,而且,对齐仍然重要。

除法

这家伙总让每一个人吃惊。打开 ARM 结构手册(如果你还没有,请看「NEON on iPhone 入门」的结构概览那节),找到整数除法指令。去吧,我等你。找不到?正常正常,根本没有的。是的,ARM 结构不支持硬件整数除法,必须通过软件执行。如果你编译下面的代码:

int ThousandDividedBy(int divisor){    return 1000/divisor;}

在汇编代码中,你会看到编译器插入了一个调用函数的「___divsi3」— 这是一个系统函数,用来执行软件除法(注意,除数不能恒定,否则除法可能会被转换为乘法)。这意味着,在 ARM 上,整数除法实际代表了操作系统的性能。

「不过,」看完手册归来,你也许会说:「你错啦!里面有 ARM 除法指令,甚至还有两个呢!在这里,sdiv 和 udiv!」不好意思给您颇凉水啦,这些指令只可用于 ARMv7-R 和 ARMv7-M 配置(分别指实时和嵌入式环境 — 例如马达的微控制器和手表),iOS 设备用的 ARMv7-A 不支持,很抱歉!

GCC

GCC 生成的 ARM 代码质量之糟已不是秘密。在其他一些基于 ARM 的平台上,专业开发者使用 ARM 自家提供的工具链 — RVDS。 不过,RVDS 不支持 OSX 用的 Mach-O 运行时,只支持 ELF 运行时,所以在 iOS 平台上没辙。但至少还有 GCC 的替代品,比如现在可以用 LLVM。虽然我没怎么测试,但是当使用 LLVM 的时候,至少看到了 64 位整数码的显著改进(这一点,GCC 在 ARM 上尤其弱)。假以时日,LLVM 全面超越 GCC 可以指望。

你瞧,现在你是更好的 iOS 开发者了!

[原文链接;作者: Pierre Lebeaupin]

扫描二维码推送至手机访问。

版权声明:本文由知了博客发布,如需转载请注明出处。

本文链接:https://www.webgou.info/?id=352

标签: ARM
分享给朋友:

“iOS 开发者应该知道的 ARM 结构” 的相关文章

详解Android组件的使用

Android开发平台是开放的平台,而位于四层框架顶端的应用开发,必然涉及到Android组件。本文将为大家详细介绍Android组件。组件(Component),在谈及所谓架构和重用的时候,是一个重要的事情。很多时候都会说基于组件的软件架构,指的是期望把程序做乐高似的,有一堆接口标准封装完整的组件…

什么是DVB-H

   DVB,数字视频广播Digital Video Broadcasting的缩写, 是由DVB项目维护的一系列国际承认的数字电视公开标准。DVB-H标准是建立在DVB和DVB-T两个标准之上的标准。DVB-H系统并不是简单的将数 据广播和DVB-T融合在一起,这主要是因为D…

Android垃圾回收实质内容解析

Android手机操作系统中的代码编写方式对于有基础的编程人员来说是比较容易的。因为它是基于Linux平台的操作系统。我们在这里为大家介绍的是Android垃圾回收这一机制,以加深大家对这一系统的了解。个人觉得sp和wp实际上就是Android 为其c++实现的自动垃圾 回收机制 ,具体到内部实现…

Android SQLite数据库应用技巧分享

Android操作系统中内置了SQLite数据库!我们在应用程序开发中都需要对数据库进行相关操作。在这里大家就一起来看看有关Android SQLite数据库的应用技巧,方便将来的应用。SQLite本身就是一个很小型的数据库!数据库存储的位置在data/data/<项目文件夹>/data…

Android文件系统深入剖析

用过Linux的朋友可能对开源的系统有一个非常好的印象。那么在日益成熟的手机操作系统中,有没有一款像Linux一样的开源系统呢?谷歌公司就退出了一款基于Linux平台的Android 操作系统来满足人们的需求。在这里大家可以先来了解一下Android文件系统的相关内容。1、Android文件系统的结…

使用ServerSocketChannel实现的file

http://blog.csdn.net/java2000_net/archive/2008/06/10/2529015.aspxhttp://www.java2000.net/viewthread.jsp?tid=6079...…

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。