http://news.cnblogs.com/n/68903/

我在写「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)时,它可能会令人吃惊。事实上,有时你会发现,调试器会进入状态为假的条件区块(ifblock,例如早期的错误回报),或者进入 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 开发者了!

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

  1. PHP开发者该知道的5个Composer小技巧

    Composer 是新一代的PHP依赖管理工具.本文介绍使用Composer的五个小技巧,希望能给你的PHP开发带来方便. 1. 仅更新单个库 只想更新某个特定的库,不想更新它的所有依赖,很简单 co ...

  2. iOS 开发者必知的 75 个工具(译文)

    原文地址:http://benscheirman.com/2013/08/the-ios-developers-toolbelt (需FQ)   如果你去到一位熟练的木匠的工作室,你总是能发现他/她有 ...

  3. iOS 开发者必知的 75 个工具

    你可以从软件开发者如何使用工具中看出他水准如何.有经验的开发者精于使用工具.对你目前所使用的工具不断研究,同时了解一些替代品的使用,当你目前所用的工具无法满足你的需要时可以填补空缺. 记住了这些,我将 ...

  4. 信息图:iOS 7开发者需要知道的事

      如果你想为iOS 设备开发app,你需要知道如何与软件交互,如何设计,你还要知道苹果独特的开发理念和开发工具.真正的能力还需要成功地从其他行业领域借鉴核心概念.最后把所有这些东西糅合进你的信息库中 ...

  5. 开发者需要知道的iOS 12

    总体概况 iOS 12总体来看是对现有iOS的一次改进,并没有太多突破性的功能或者框架,但是Apple在底层做了很多优化的工作,优化了性能,提供了更强大的安全性,增强了AR.Siri体验,让人工智能更 ...

  6. 苹果强制使用HTTPS传输了怎么办?——关于HTTPS,APP开发者必须知道的事

    WeTest 导读 2017年1月1日起,苹果公司将强制使用HTTPS协议传输.本文通过对HTTPS基础原理和通信过程内容的讲解,介绍APP开发者在这个背景下的应对办法. 几周前,我们在<htt ...

  7. Web 设计与开发者必须知道的 15 个站点

    新闻来源:catswhocode.com公司博客整整一个月没有更新了,最近一段时间,全公司都忙于两件事,为海尔集团做定制,为一个合作伙伴做 OEM,终于有了眉目.工作期间,常用到一些工具与帮助站点,今 ...

  8. iOS开发应该知道的7个编程概念

    对流行工具(如Xcode)和编程概念(如视图控制器)的高级讨论,这些对iOS开发本身很有用. 1. Xcode Xcode是iOS应用开发社区所见过的最通用的IDE.由于集成开发环境来自Apple,它 ...

  9. 【转载】PHP 开发者该知道的 5 个 Composer 小技巧

    Composer是新一代的PHP依赖管理工具.其介绍和基本用法可以看这篇<Composer PHP依赖管理的新时代>.本文介绍使用Composer的五个小技巧,希望能给你的PHP开发带来方 ...

随机推荐

  1. Qt5 OpenGL框架

    #ifndef MYRENDERER_H #define MYRENDERER_H #include <QOpenGLContext> #include <QOpenGLFuncti ...

  2. 创建 .gitignore 文件过滤规

    文件 .gitignore 的格式规范如下: 所有空行或者以注释符号 # 开头的行都会被 Git 忽略. 可以使用标准的 glob 模式匹配. 匹配模式最后跟反斜杠(/)说明要忽略的是目录. 要忽略指 ...

  3. NOIP2005-普及组复赛-第三题-采药

    题目描述 Description 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了一个难题.医师把他带到一个到处都是草药的山 ...

  4. D - Digging(01背包,贪心)

    D - Digging Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit St ...

  5. 10.按要求编写Java应用程序。 (1)创建一个叫做People的类: 属性:姓名、年龄、性别、身高 行为:说话、计算加法、改名 编写能为所有属性赋值的构造方法; (2)创建主类: 创建一个对象:名叫“张三”,性别“男”,年龄18岁,身高1.80; 让该对象调用成员方法: 说出“你好!” 计算23+45的值 将名字改为“李四”

    package com.hanqi.test; public class People { private String name,sex; private int age; private doub ...

  6. 3.1 Data Member的绑定

       文章开始提出了一段示例代码,并讨论了返回哪个x的问题.然后 a)   给出了我们普遍认为正确的回答,并肯定了这个想法: b)   提醒大家,这在以前的编译器实现中,并非正确. 在早期的实现中,首 ...

  7. Oracle结构知识学习+部分函数实例

    一 Oracle的结构1 实例(instance) 是内存和后台进程的集合, 数据库是数据的物理储存;实例和数据库的关系是一对多的关系;2 多个实例同时驱动一个'数据库'的架构 叫集群(RAC)3 O ...

  8. UESTC 1217 The Battle of Chibi

    dp+树状数组优化. dp[i][j]表示以a[i]结尾,最长上升序列长度为j的方案数.dp[i][j]=sum{dp[k][j-1]} 其中k<i&&a[k]<a[i]. ...

  9. ZUFE OJ 2288 God Wang I

    Description God Wang 是ZUFE的神犇,有一天他想到一种神奇的变换,并且将它命名为GodW变换 对于一个数字n,该变换后的值GodW(n)为,先令X=n 第一步,如果X为个位数,G ...

  10. python 字典排序,列表排序详细

    在程序中使用字典进行数据信息统计时,由于字典是无序的所以打印字典时内容也是无序的.因此,为了使统计得到的结果更方便查看需要进行排序.Python中字典的排序分为按“键”排序和按“值”排序. 1.按“值 ...