iOS 開發者應該知道的 ARM 結構

2022-09-12 16:33:24 字數 4496 閱讀 5964

我在寫「neon on iphone 入門」的時候,曾以為讀者已經比較了解 ios裝置的處理器知識。然而,看過網上的一些討論,我才發現,原來這些知識並不普及,我的錯。此外,我覺得了解這些東西對 iphone程式設計有益(不僅僅針對喜歡 neon 的人),即便你用的是 objective-c,雖然,不了解也無礙工作,但這些知識會讓你成為乙個更好的iphone 程式設計師。

基礎

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。至於如何偵測,我馬上會談到。

如果不懂得如何利用,即使裝置支援 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。如果你看到無效或無意義的指令,最好互相對調一下。

對齊

除法

這傢伙總讓每乙個人吃驚。開啟 arm 結構手冊(如果你還沒有,請看「neon on iphone 入門」的結構概覽那節),找到整數除法指令。去吧,我等你。找不到?正常正常,根本沒有的。是的,arm 結構不支援硬體整數除法,必須通過軟體執行。如果你編譯下面的**:

int thousanddividedby(int 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 結構

這是一篇圍繞 ios 來介紹 arm 結構的文章,用詞簡單,邏輯清楚,偶見幽默。非開發者也值得一讀,權當增長知識。我在寫 neon on iphone 入門 的時候,曾以為讀者已經比較了解 ios 裝置的處理器知識。然而,看過網上的一些討論,我才發現,原來這些知識並不普及,我的錯。此外,我覺得了解這...

9個php開發者應該知道的PHP庫

the recaptcha 庫 讓你可以為 建立高階的captcha系統,這個系統其實是用來生成驗證資訊的,甚至包括語音驗證,下圖就是個好例子。當然還有recaptcha 服務可以使用,其提供易用的免費api,值得在你的 試試。文件 介紹 akismet json是人類能容易理解的資訊傳遞格式。不過...

用友雲開發者中心,你應該知道的那些事

2018開發者中心產品不斷進行架構公升級優化,同時也在不斷完善產品能力,目前已支撐內部大量雲產品的執行,下面給大家介紹一下新增的幾大能力 一 一體化的計算資源管理 1.提供資源池使用率看板,資源池的記憶體分配和實際使用率對比情況,記憶體消耗情況一目了然,基於這個資料,可以更好的優化每個應用的記憶體分...