ubuntu c++ 实现自动回车键功能_从X86到ARM,实现C和C++语言90% Code自动迁移
x86 與 ARM 之爭,已經貫穿了很長時間,過去一直是 x86 架構比較受到市場和開發者的歡迎。但是自從移動互聯網、物聯網和邊緣計算興起之后,ARM 似乎已經找到最適合自己生存的土壤。
架構之爭的平臺技術拐點,已然來臨。
現在,每個人手上都有一臺智能計算終端,移動應用逐漸云化,5G 催生了云游戲的誕生;Web 應用的加密性越來越重要,HTTPS 流量越來越大;大數據分布式并行計算成為主流等,這些都讓 x86 架構的不足逐漸顯露出來。
所以才會有現在所遇到的情況,即不得不從 x86 上的應用遷移至 ARM 上。但也正因為這是兩個完全不同的平臺,所以在遷移過程中會遇到各種各樣的問題。
以下內容經由 InfoQ 編輯整理自DevRun開發者沙龍中張永正和楊少洪老師的分享。
1?鯤鵬軟件遷移概述和方法論
?首先要搞懂:為什么要遷移?
上圖所示為程序執行的過程和對應的計算棧。任何一臺計算機都是由硬件和軟件組成的,類似于最底層的基礎物理原材料、晶體管、寄存器、微架構等都屬于硬件層面。而軟件層面則特指由高級語言、匯編語言開發的應用程序。要執行這些應用程序,需要底層 CPU 支持由匯編器形成二進制的機器碼(由指令和數據組成)去運行。
因此就需要底層計算平臺能夠支持該 CPU 的指令才可以,這也是在 x86 和鯤鵬編譯的區別之處。
在 x86 和鯤鵬上編譯之后的指令差異是哪些?可以參照下圖左側,顯而易見這是一套非常簡單的代碼,分別在 x86 和鯤鵬上編譯之后形成三點指令差異:
首先是匯編不同,x86 上是兩條 mov 指令,通過把 A 和 B 的變量從內存當中取到寄存器,并將兩值相加,再由一條 mov 指令寫回內存;鯤鵬上則是通過兩條 ldr 指令、一條 add 指令以及一條 str 指令完成整個過程。
其次是指令長度不同,x86 上 mov 指令是 24 位的,ldr 指令是 16 位的,在鯤鵬上則都是 32 位;第三則是寄存器不同,x86 和鯤鵬處理器使用的向量寄存器不同,其向量指令級也存在差異。
鯤鵬處理器與 x86 處理器的指令差異
這也正是做遷移的原因,因為在 x86 上所編譯出來的應用程序因為有以上三點的不同,因此無法在鯤鵬上直接運行。
?從 x86 到鯤鵬,遷移五步走戰略
從大量的實踐中得以總結出一些規律和方法,主要分為以下 5 個步驟:
1、遷移準備,主要以收集硬件信息和軟件棧信息為主;
在這期間,主要收集硬件和軟件信息。硬件方面的信息主要是收集芯片和服務器的型號,從而方便提供配置性能差不多的鯤鵬服務器;其次是收集軟件棧信息,主要分為操作系統、虛擬機、中間件、編譯器、上層依賴的開源軟件、商業軟件、業務軟件等信息。
2、遷移分析,對收集到的信息和軟件棧做初步分析,判斷是否真正需要遷移,評估遷移的工作量;
下圖左側就是一個非常完善的技術棧,底層有芯片,中間層為 OS、虛擬機、編譯器等相對應的運行環境。上層是業務軟件,分為開源、自研和商用軟件。
開源軟件的遷移相對較為簡單,其中一部分開源軟件在 ARM 上已經被編譯好的包,直接下載即可。即便沒有現成的編譯成果,自行下載原碼進行編譯也并不復雜方便;自研軟件的遷移需要注意語言類型的差異,編譯型語言是需要重新編譯之后才能運行在新環境上,但是對于解釋型的語言來說就沒有重新編譯的需要,只要更換所依賴的虛擬機就可以;商用軟件則較為麻煩一些,首先可以通過聯系廠商獲取它對應 ARM 架構下的軟件版本,如果沒有的話就需要尋找有類似功能的軟件做替換。
此外像運行環境、虛擬機、編譯器和操作系統這些也是要進行替換,但是這些并非需要重新編譯,因為在華為云鯤鵬論壇內有軟件倉庫,可以直接去軟件倉庫下載由鯤鵬官方所做的經過驗證的版本。
3、編譯遷移,分析完成之后就可以著手遷移工作,主要分為代碼遷移和軟件包遷移;
遷移分析之后可以進行代碼的編譯和打包,編譯主要涉及兩個到代碼遷移和軟件包遷移。
其中代碼遷移需要區分語言,像 C/C++ 和指令級的差異是比較大的,因此在 x86 上編譯出來的應用程序無法在在鯤鵬上直接使用,因此要在鯤鵬上重新編譯才可以。此外編譯型語言所涉及的修改點相對更多,因為代碼當中有可能蘊含一些對指令級相關的宏定義或者功能性函數;但是對于 Java/Python 這種解釋型語言來說就會簡單很多。如果是純 Java/Python 的程序,就無須做編譯,因為本身的虛擬機已經對指令級進行了屏蔽,只要更換虛擬機就可以。
對于軟件包遷移來說,首先需要掃描該軟件包是否存在依賴庫或者依賴的可執行程序,這些庫和可執行程序如果是用 C 語言寫的是需要重新編譯的,編譯之后重新把軟件包打包即可。
4、性能調優,驗證完成之后對性能指標進行測試,進行性能調優;
由于大部分軟件對性能都有要求,因此在遷移完成之后需要對性能進行調優,這里總結了【建立基準 - 壓力測試 - 確定瓶頸 - 實施優化 - 確認效果】這五步法。
首先需要建立調優基準,該基準根據當前的硬件配置、組網、測試模型來做綜合評估,以建立合理的條有目標;其次在調優目標建立后,通過壓測工具對軟件或系統進行加壓,在加壓過程中暴露性能瓶頸,確定瓶頸之后對瓶頸進行優化;第四,注意在優化過程中要及時記錄,因為優化并不一定是正向的,出現負向優化時需要及時回退;最后在優化措施實施完成后,需要重新啟動壓力測試工具以確認優化效果。
這個過程并非一蹴而就,有可能其中某個步驟需要經過多次循環之后才能達到既定目標。
5、測試與認證,保障商用上線。
和日常軟件開發一樣,需要對其進行功能、性能、長穩等層層測試,以確保能達到商用標準。此外也可以拿軟件和系統到鯤鵬上做鯤鵬展翅認證,其可以擴展應用的軟件使用空間并能夠加入鯤鵬生態。
2?C/C++ 代碼編譯原理及構建流程
?編譯型語言,從源碼到可執行程序的歷程
C、C++、GO 都是非常典型的編譯型語言,編譯型語言所開發的程序從 x86 平臺移植到鯤鵬平臺時一般都需要重新編譯才能運行,這點上文也已經提到了。那么為什么需要重新編譯才能運行呢?接下來舉個簡單的例子。
上圖為 test.c 的源碼文件,首先經過預處理,把代碼里面以 # 號開頭的代碼片斷編譯為預處理文件,預處理文件再經由編譯生成匯編代碼。匯編代碼經過匯編器生成目標文件,這也就是常說的機器碼。然而機器碼是無法直接運行的,所以需要聯接動態庫或者靜態庫來最終生成可執行文件。
?編譯構建的過程
從代碼工程的角度看,其可以分為兩類:一類是編譯構建的腳本,二是源碼。在編譯構建的腳本中一般會存在 Makefile、cmakelist 等一系列腳本文件,C 和 C++ 的源碼一般是有 src、tests 等文件。
那么編譯構建腳本類文件在遷移過程中會涉及哪些因素?一般會涉及到編譯選項的移植,源碼類文件會涉及到編譯宏,另外可能還會有編譯器自帶的 Builtin 函數的移植、SSE intrinsic 函數移植等。需要強調的是這里提到的是遷移過程中可能會涉及到的移植項,但在實際遷移過程中可能只需要編譯選項或修改編譯宏就可以。
C/C++ 代碼編譯構建過程
首先要從獲取源碼開始,可以通過 GitHub 等開源社區來獲取;其次需要選擇所需的編譯環境,就是安裝編譯器 gcc 等;之后根據源碼的編譯腳本生成 Makefile 文件,再用 Makefile 編譯生成可持續文件。如果這部分代碼之中有依賴 x86 平臺的 SO 庫,那么這部分的依賴庫是需要重新編譯替換的。在編譯完成之后進行安裝部署,之后進入到實際的系統之中進行測試。
?典型的移植類問題
在對編譯構建的流程有基本理解后,就需要深入了解實際遷移過程中所涉及到的各種移植項。
編譯腳本和編譯選項的移植
以上圖為例,其中 x86 下 -m64 代碼的主要功能是將應用程序編譯為 64 位,對應到鯤鵬上是用 -mabi=lp64 的編譯選項。上文有提到這編譯選項需要在腳本中修改,對應的 Cmakelists 里有可能存在 add_defin 等多種定義方式。
再看常用的數據類型移植,眾所周知 x86 平臺上默認的 char 類型是一種有符號的類型,對應到鯤鵬上則是無符號類型。因此在移植過程中需要顯示定義并將 char 類型定義為有符號。一種方法是在源代碼里加上 signed char,但是缺點是可能改不全從而引發一些不可預知的問題。另一種方法是直接用 fsigned-char 來修改,在不同架構下差異化的編譯選項也可以通過 gcc 文檔進行查詢。
編譯宏的移植
如果有相同的代碼片斷在不同平臺下可能會存在不同分支,需要將不同架構下的性能優勢發揮到較高水平。但是對于編譯器來說,如何才能得知要編譯哪些分支代碼呢?這就是編譯宏的作用。
如果大家對大型開源軟件有接觸過,相信對上圖這些編譯選項都不會陌生。gcc 編譯器所自帶的 x86 編譯選項就是 x86_64,對應到鯤鵬平臺上是 ark-64。當然除了 gcc 編譯器自帶的自定義宏,在實際場景中也需要對自研代碼進行自定義,在鯤鵬上也是需要進行自定義替換。
不過針對編譯宏的替換只是其中一個方面,其最重要的是對編譯宏下面的代碼實現移植。x86 代碼上有些編譯器自帶自定義宏,比如 smd 屬性相關的宏在 x86 上是 SSE 開頭的宏,對應到鯤鵬平臺上就需要自定義它的編譯宏和所相對應的分支。
3.Builtin 函數移植
Builtin 函數是編譯器自帶的函數,其在實際遷移項目中相當常見,主要是 crc32 校驗值的計算。需要移植的普通 builtin 函數實際并不多,大部分需移植的 builtin 函數集中在 SSE intrinsic 函數內。
通過上圖可以看到在 x86 平臺上其和在鯤鵬平臺上是類似的,從命名來看有差異的地方就只存在于架構。
內聯匯編函數的移植
內聯匯編對于部分開發者來說平時接觸的會比較少,所以又可能會感覺到陌生。
上圖列舉了將字節序進行反序的例子,比如 0X56781314 反序輸出的是 0X14137856,x86 上對應的是 bswap 指令,鯤鵬對應的是 rev 指令,其它有些操作和寄存器都是基于內聯匯編的語法規則進行替換的。上圖的另一個例子是 Builtin 函數,列舉了內聯匯編轉換用鯤鵬上面的 Builtin 函數做替換的例子。比如 popcount 是對二進制數里面的 1 進行計數,對應到鯤鵬平臺上所替換的是 popcountll。
5.SSE intrinsic 函數移植(SIMD 技術)
關于 SSE intrinsic 函數的移植,在這之前需要先了解 SIMD 的技術。SIMD(Single Instruction Multi Data) 是一種單指令處理多數據流的并行處理技術,能夠在批量數據操作時進行向量 化運算加速,具有較高的執行效率,在多媒體處理、矩陣運算等場景都有廣泛的應用。
Intel 的 SIMD 擴展指令統稱 SSE,主要分為三類,MMX 是 64 位寄存器,SSE 到 SSE4 是 28 位的,三是 AVX256 和 AVX512。鯤鵬基于 SIMD 的技術發展比較成熟,現在有些基于開源量的 NEON 庫主要是在圖象處理和視頻處理層面。
6.SSE intrinsic 函數移植(MMX/SSE)
經過調用編譯器就能夠基于 C 函數調用完成對 SIMD 技術的應用,極大方便了開發者。
以上圖為例,在 x86 上用的是 -m64 的向量,add 是關鍵字,而且這是加法運算,后面 32 是數據類型。對應到鯤鵬上是 int32×2 然后再做加法運算,這常用的 C 函數規則是類似的。針對 SSE 指令,從內存中加載 4 個單精度浮點數據到寄存器,x86 是 load,對應到鯤鵬用的是 vld1q。
7.SSE intrinsic 函數移植(AVX)
AVX 指令和 MMX 類似,只不過其位數不同。以 AVX 指令使用了 256 位寄存器運算為例,向量 A 和向量 B 中分別存儲了 8 個單 精度浮點型 (32 位)。該指令將向量 A 和向量 B 中的 8 個數值分別相加,并將結果以返回值的形式返回 (結果中依然是 8 個單精度浮點型數據) ,最后再從向量寄存器中分別取出 8 個單精度浮點數累加得到結果。
對應到鯤鵬方面,鯤鵬處理器采用精簡指令集,使用 128 位寄存器實現 SIMD(Single Instruction Multi Data) 計算。在實現本例 16 個浮點數的相加時,通過兩條 vaddq_f32 指令來分別完成,每條指令完成兩組共 8 個浮點數算,最后再從向量寄存器中分別取出 8 個浮點數累加。
3?寫在最后
對于開發者而言,代碼和軟件遷移是一套必須要掌握的技能。尤其是各種智能終端數量暴漲,物聯網飛速發展的當下,x86 平臺已經難以適應全生態的發展,從 x86 遷移至 ARM 平臺,正是現在的大勢所趨。而鯤鵬生態,則為每一位有遷移需求的開發者提供了最便利的工具和環境。沒有哪一款平臺是最好的,只有最適合業務的那款平臺,從 x86 到 ARM,答案正在逐漸清晰。
溫馨提示:
請搜索“AI_Architect”或“掃碼”關注公眾號跟蹤技術動態,點擊“閱讀原文”獲取更多技術和精彩內容。
總結
以上是生活随笔為你收集整理的ubuntu c++ 实现自动回车键功能_从X86到ARM,实现C和C++语言90% Code自动迁移的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python bootstrap-fil
- 下一篇: ajax参数中字符串最大长度_6.7 C