將 Linux on x86 應用程序移植到 Linux on Power 的指南

https://www.ibm.com/developerworks/cn/linux/l-port-linux-on-x86-application/

將 Linux on x86 應用程序移植到 Linux on Power 的指南

Artis Walker, AIX/Linux 移植工程師, IBM     Steven Frischknecht, 技術支持專家, IBM

2014 年 8 月 08 日

本文將介紹如何使用以下直觀的分步流程將 Linux® C/C++ 應用程序從 x86 平臺(Intel® 或 AMD)移植到 IBM® PowerLinux™。我們首先要了解需要爲移植做的準備工作,然後我們將介紹一些讓 32 位和 64 位代碼在 PowerLinux 上運行的實現技巧。

簡介

在大多數情況下,將 Linux 應用程序從 x86 平臺移植到 Linux on Power 很簡單,因爲兩個平臺都基於來自 Novell SUSE 或 Red Hat 的同一個 Linux 版本。移植常常只需要執行一次 GNU Compiler Collection (GCC) 再編譯,對一些編譯器和鏈接器開關稍作更改。

但是,爲解決突然出現的未知問題做好準備是擁有一個成功端口的重要優勢。通常,如果重點關注的是移植,將優化放在以後進行,那麼本文中介紹的指南、技術和工具可以最大程度地降低(甚至可能消除)移植到 Linux on Power 的難度。

在某些情況下,當您單獨在一種特定硬件架構(比如 x86)上設計應用程序時,有時可能需要做一些額外的修改。本文會重點介紹與將 x86 系統上運行的 LInux 應用程序移植到基於 IBM POWER® 處理器的系統相關的一些差異,本文還提供了讓 x86 代碼爲移植到 Linux on Power 做好準備的一些建議。

  • 您應用程序的字節順序
  • 32 位和 64 位數據類型(長度和對齊方式)
  • 理解可用的編譯器選擇 - GCC(分發和 Advance Toolchain)和 IBM XL C/C++ 編譯器。
  • 使用 IBM SDK for Linux 和 IBM Rational Developer for Power Systems - 使用 GNU C/C++ 編譯器構建大型程序。

應用程序開發人員可以在 Power Linux 上使用一些高級工具和技術,其中包括 IBM Software Development Kit for PowerLinux (SDK),這是一個免費、基於 Eclipse 的集成開發環境 (IDE)。該 SDK 集成了 C/C++ 源代碼開發與 Advance Toolchain、鏈接後優化和經典的 LInux 性能分析工具(包括 OProfile、Perf 和 Valgrind)。

計劃移植

將應用程序移植到一個新平臺時,恰當的計劃不可或缺。要爲移植做好充足的準備,您應該:

  1. 以可視方式掃描代碼,尤其需要注意那些可能錯誤匹配的賦值運算,以及位操作和比較。
  2. 理解 IBM Power® 平臺架構要點,以及 x86 與 IBM POWER 處理器架構之間的差異 - 特別是您應用程序的字節順序。
  3. 確定使用哪個 Linux on Power 發行版:Red Hat Enterprise Linux 或 Novell SUSE Linux、CentOS 還是 Ubuntu。
  4. 考慮使用 GNU Make 構建系統。
  5. 確定要使用哪個編譯器:GCC 或 IBM XL C/C++ for Linux。
  6. 獲取一個用於開發的 IBM Power 服務器。下面列出了一些選擇:
  7. 查看下面的程序,確定是否有個程序可以幫助加快移植並取得成功。

Power Development Cloud
這個完全自動化的 IBM Power Development Cloud 是一個免費的平臺即服務 (PaaS),可用於使用 IBM 平臺開發和演示軟件解決方案。您可以利用 IBM Linux on Power 開發堆棧,它已預先配置和安裝了針對從 x86 移植到 Linux on Power 的開發工具。

IBM Hardware Mall – 租賃和折扣
Hardware Mall 旨在激勵獨立軟件供應商 (ISV) 在 IBM 平臺上開發軟件解決方案,爲他們提供了系統硬件和適用軟件的重要購買折扣和低租賃費率。

IBM Systems Application Advantage for Linux (Chiphopper)
IBM Chiphopper™ 產品是一個應用程序移植或重新託管程序,旨在免費幫助 IBM 業務合作伙伴啓動、測試和支持其在競爭平臺上運行的現有 Linux 應用程序向運行 LInux 和中間件平臺的 IBM Power Systems™ 的移植。

IBM Innovation Center
IBM Innovation Center 提供了培訓和一對一指導,涉及從構建到營銷和銷售解決方案的方方面面。IBM Innovation Center 團隊隨時準備幫助您實現開發目標

您可以在 解決方案開發硬件 網站上找到並利用所有這些服務。

理解 Power 平臺的差異

您要移植到的 Power 硬件平臺,確定了您在編譯應用程序時想要使用的優化選項。您應考慮以哪個處理器版本爲基礎,例如 IBM POWER5、IBM POWER6®、IBM POWER7® 或 IBM POWER8™。爲基於 POWER 處理器的系統開發一般性的代碼非常容易,最終的應用程序將在多個系統上正常運行。針對 IBM 較新的幾代 POWER 產品開發應用程序也很容易。一個重要的選擇是選擇使用 GCC 編譯器還是 XL C/C++ 編譯器。更多細節將在本文後面討論。

編譯器:

多年來,GCC 技術已得到顯著改進,能夠爲基於 IBM POWER7 和 POWER8 處理器的服務器提供經過調優的應用程序。此外,如果性能是終極目標,那麼 IBM XL 編譯器可以實現更高的性能。

  • 如果在 Linux on x86 上使用 GCC,推薦使用 GCC 完成初始移植。Linux on Power 上的 GCC 技術已顯著改進,熟悉 GCC 會使得以它爲基礎進行移植變得更輕鬆。
  • 如果需要的話,您還可以使用 IBM XL C/C++ 編譯器。
  • 如果在 Linux on x86 上使用 Java™,那麼推薦從 IBM 網站下載並使用合適的 IBM Java 工具包。

有關 Power Systems 上的虛擬化的更多信息,請查閱 Linux on Power:針對開發人員的概述(developerWorks,於 2011 年 7 月更新)或參閱 執行簡報 瞭解使用 Power Systems 進行服務器整合的可能性。

編譯器標誌:

移植到基於 POWER 處理器的服務器時,可以在 XL C/C++ 編譯器中對 -qarch-qtune 使用不同標誌,或者在 GCC 編譯器編譯器中對 -mcpu-mtune 使用不同標誌來優化應用程序。但是,對於初始移植,爲了最大限度降低難度,最好使用基於 Power 的最常見的 –qtune=ppc–mcpu=power。具體的標誌和使用它們的時機將在本文後面討論。

確定使用哪個 Linux on Power 發行版

來自 Novell 和 Red Hat 的最新的操作系統產品(本文發表時)提供了完成企業級部署所需的技術、支持和可靠性。首選產品的選擇通常在一個公司或組織內要考慮的事項。

  • SUSE Linux Enterprise Server (SLES)。有關 SLES 的更多信息,請參閱 參考資料 部分。
  • Red Hat Enterprise Linux (RHEL)。有關 RHEL 或 CentOS 的更多信息,請參閱 參考資料 部分。

使用 SLES 還是 RHEL 的決定不會對移植過程產生直接影響。但是,SUSE 和 Red Hat 擁有不同的發佈和更新週期,在二進制兼容性上具有不同的策略,從長遠角度講,這些可能影響您的應用程序更新決策。此外,Red Hat 和 SUSE 發行版按相同的時間表發佈對 Intel® 和 Power 的支持。

遷移到 GNU Make

如果目前尚未使用 GNU Make 構建應用程序,那麼您可以考慮遷移到該應用程序。一種不錯的編程實踐是:使用一個能控制可執行文件的生成的工具,而不依賴於腳本或直接調用編譯器來生成可執行文件。大多數 C 和 C++ 編程人員都使用 Make 作爲這樣的工具。切換到 GNU Make,這樣您就能通過同一個構建控件 makefile 在多個平臺上執行一致的構建操作。GNU Make 同時具有 SLES11 和 RHEL6 版本。有關 GNU Make 的更多信息,請參閱 “參考資料” 部分。

假設您的 Linux on x86 應用程序是使用 GCC 構建的,那麼我們推薦首先使用 GCC 在 Power 上重新編譯的簡單方法。後續步驟將會添加簡單的調優和優化選項供考慮。如果需要的話,接下來的一個步驟將是考慮 IBM XL 編譯器,以便實現針對特定 IBM Power Architecture® 級別調優的更高性能。

理解 x86 與 Power Architecture 衍生產品之間的差異

在將 x86 Linux 應用程序移植到 Power Systems 之前,您應該知道一些特定於架構的差異。在接下來的兩節中,我們將詳細介紹一些特別值得注意的架構差異:

  • 字節順序
  • 32 位和 64 位應用程序環境中的數據類型長度
  • 架構中的數據對齊方式差異

我們稍後將會介紹對比 x86 和 Power 應用程序時的一些額外的考慮因素。優化考慮因素示例包括使用多個線程優化吞吐量,評估簡單鎖定模式的使用,等等。

字節順序

儘管 POWER 處理器可同時支持高位優先和低位優先(字節排序模式),但最新的實現都使用一種高位優先架構,而 x86 處理器使用了一種低位優先架構。本節介紹字節順序問題,以及處理它們的技術。在將低級 應用程序、設備驅動程序或數據文件從 x86 架構移植到 Power Architecture 期間,開發人員通常會遇到一些字節排序問題。高級 應用程序在移植時很少遇到字節順序問題,但在從 x86 移植到 Power 時需要解決此問題。

字節排序會影響整數和浮點數據,但不會影響字符串,因爲它們會保持編程人員看到和想要的字符串順序。所以,原始 ASCII 文本文件可正常使用,但與機器獨立的數據文件可能受到硬件字節順序問題的影響。例如,JPEG 文件被存儲爲高位優先格式,而 GIF 和 BMP 文件被存儲爲低位優先格式。

高位優先和低位優先

字節順序指數據元素和它的各個字節在內存中存儲和尋址的方式。在多位數中,具有更高數量級的位被視爲更重要的位。例如,在 4 位數 8472 中,4 比 7 更重要。類似地,在多字節數字數據中,字節持有的值越大,它就越重要。例如,十六進制值 0x89ABCDEF 可拆分爲 4 個字節:0x89、0xAB、0xCD 和 0xEF,分別具有算術值 0x89000000、0xAB0000、0xCD00 和 0xEF。顯然,字節 0x89 是最大的值;因此它是最重要的字節,而字節 0xEF 是最小的部分,因此是最不重要的字節。

將一個數字放在內存中時,從最低的地址開始,只有兩個有意義的選項:

  • 首先放置最不重要的字節(低位優先)。
  • 首先放置最重要的字節(高位優先)。

下圖顯示了高位優先和低位優先處理器如何將 32 位十六進制值(比如 0x89ABCDEF)放入內存中:

圖 1. 存儲十六進制值的高位優先和低位優先處理器
存儲十六進制值的高位優先和低位優先處理器

0x89 是最重要的字節,而 0xEF 是最不重要的字節。在高位優先系統上,最重要的字節放在最低的內存地址上。在低位優先系統上,最不重要的字節放在最低的內存地址上。

如果程序將一個單詞寫入內存中,然後讀取相同位置作爲一個單詞,那麼字節排序不存在問題,因爲在採用一致的方式引用某個變量時,看到的會是同一個值。如果程序嘗試一次讀取同一個值的一個字節(在寫入一個單詞時),根據處理器採用低位優先還是高位優先格式,它可能獲得不同的結果。

IBM POWER 處理器系列是使用高位優先數據佈局的系統示例,而 x86 處理器系列是使用低位優先數據佈局的系統示例。識別依賴於字節順序的代碼段,並將它們轉換爲等效的高位優先格式,這在將 x86 應用程序遷移到 Power 平臺期間很重要。

處理字節順序

本節將介紹如何識別代碼中依賴於字節順序的區域,並將它們轉換爲正確的字節順序格式的方法。

依賴於字節順序的代碼
數據引用上的非一致性是 C 語言的優勢,這使得它在編程系統級軟件(包括操作系統和設備驅動程序)中變得很流行。此優勢包括類型轉換、指針操作、union、位字段、結構和靈活的類型檢查。但是,同樣是這些特性,它們也可能是字節順序可移植性問題的來源。作爲示例,請考慮以下兩個代碼清單:

清單 1. 使用指針的非一致數據引用

#include <stdio.h>
int main(void) {
  int val;
  unsigned char *ptr;

  ptr = (char*) &val;
  val = 0x89ABCDEF; /* four bytes constant */
  printf("%X.%X.%X.%X\n", ptr[0], ptr[1], ptr[2], ptr[3]);
  exit(0);
}

清單 2. 使用 union 的非一致數據引用

#include <stdio.h>
union {
  int val;
  unsigned char c[sizeof(int)];
} u;

int main(void) {
  u.val = 0x89ABCDEF; /* four bytes constant */
  printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
  exit(0);
}

在 x86 系統上,結果爲:

 EF.CD.AB.89

在基於 POWER 處理器的系統上,結果爲:

 89.AB.CD.EF

字節順序問題表現爲 val,它從最重要的字節開始逐個字節地讀取數據。

如何確定系統的字節順序

在 Linux 中,GNU_C 預處理器通常會自動提供一組常用的預定義宏,它們的名稱以雙下劃線開始和結束。一組對確定使用的系統的字節順序最有用的宏是 __BYTE_ORDER__、__ORDER_LITTLE_ENDIAN__ 和 __ORDER_BIG_ENDIAN__。

/* Test for a big-endian machine */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
/* Do big endian processing */

編寫字節順序中立的代碼

如果程序模塊在跨具有不同字節順序的平臺移植時能夠保留其功能,那麼它就是字節順序中立的。換句話說,它的功能與運行它的平臺的字節順序無關。以下是一些編寫字節順序中立的代碼的建議:

  • 使用宏和指令
    要讓代碼變得可移植,可以使用宏和條件編譯指令,如清單 3 和清單 4 所示。

清單 3. 使用指令讓字節順序效果中立化

#include <stdio.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
#define BYTE_ORDER (( htonl(1)==1) )   //  returns 1 or 0 depending on platform


union {
  int val;
  unsigned char c[sizeof(int)];
}u;

int main(void) {
  u.val = 0x89ABCDEF;
  #if (BYTE_ORDER == BIG_ENDIAN)
  printf("%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
  #else /*!BYTE_ORDER == BIG_ENDIAN*/
  printf("%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]);
  #endif /*BYTE_ORDER == BIG_ENDIAN*/
  exit(0);
}

清單 4. 使用宏交換字節(對在運行時確定字節順序很有用)

//  Useful Endian Neutral Macros

#include <endian.h>        

#if __BYTE_ORDER == __BIG_ENDIAN
// No translation needed for big endian system
#define sw2Bytes(val) val
#define sw4Bytes(val) val
#define sw8Bytes(val) val
#else
//  Little Endian:Translate
// Swap 2 byte, 16 bit values:

#define sw2Bytes(val) \
 ( (((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00) )

// Swap 4 byte, 32 bit values:

#define sw4Bytes(val) \
 ( (((val) >> 24) & 0x000000FF) | (((val) >>  8) & 0x0000FF00) | \
   (((val) <<  8) & 0x00FF0000) | (((val) << 24) & 0xFF000000) )

// Swap 8 byte, 64 bit values:

#define sw8Bytes(val) \
 ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \
   (((val) >> 24) & 0x0000000000FF0000) | (((val) >>  8) & 0x00000000FF000000) | \
   (((val) <<  8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \
   (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) )
#endif
int main(void) {
  int a=0x11121314;
  int b;
  b = sw4Bytes(a);      // b is 0x12 in LE and BE
}
  • 使用編譯時選項
    實現此目的的另一種方式是在編譯器命令行上將 BYTE_ORDER 的值定義爲 -DBYTE_ORDER=BIG_ENDIAN。在一個具有不同字節順序的新平臺上編譯時,這會消除編輯設備驅動程序或應用程序中的每個文件的需求。只需編譯用於構建該驅動程序或應用程序的 makefile。

字節順序:特定於 Linux

事實證明,Linux 內核提供了一組特定的系統宏,它們可以從低位優先到高位優先和從高位優先到低位優先執行 16、32 和 64 位交換。這些宏對從 Linux x86 移植到 Linux on Power 以及讓您大代碼對字節順序中立都很方便,無需擔憂會在代碼中編寫大量 #ifdef __LITTLE_ENDIAN 條件指令。

例如:

cpu_to_le16(u16); // converts CPU endianness 4 bytes to little-endian

le16_to_cpu(u16); // converts little-endian 4 bytes to CPU endianness

這兩個宏將一個值從 CPU 使用的字節順序轉換爲一種無符號、低位優先、32 位數字,或者執行反向轉換。例如,在 Linux on Power 系統上,le16_to_cpu(u16) 將低位優先轉換爲高位優先,而 cpu_to_le16(u16) 將高位優先轉換爲低位優先。根據使用的宏和運行的是哪個系統,如果沒有工作要做,那麼它們將會返回原始值。

這些宏和其他許多宏位於:/usr/include/linux/byteorder/big_endian.h> 和 /usr/include/linux/byteorder/little_endian.h 中。

您可以查看這些頭文件來找到您需要的宏。您還可以根據名稱中的模式推斷出它們的用途。

您可以使用預定義的 GNU_C 預處理器指令,輕鬆地包含正確的 include 文件,如下所示:

#include <endian.h>

#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
   #include <linux/byteorder/big_endian.h>
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
   #include <linux/byteorder/little_endian.h>
#else
   //  …user defined endian header
#endif

其他字節順序考慮因素

從 x86 移植到 Power 時,不依賴於外部數據文件並且遵守嚴格的 C ANSI 編程標準的高級應用程序或許能夠順利地移植到 Linux on Power,且不會出現字節順序問題。但是,字節順序是一個需要理解的重要概念,而且在代碼中識別這些漏洞的能力能夠使得向 Linux on Power 的遷移變得很容易。

此外,在下一節中,我們將重點介紹 IBM Linux SDK 中提供的其他代碼分析工具,它們可以幫助識別字節順序問題。

選擇要使用哪些遷移工具

類似於機械領域,移植工程師的 “技術” 工具箱中必須有許多工具,而且知道使用哪個工具可用來最大限度降低移植的難度。IBM Linux on Power SDK 和 IBM Rational Developer for Power Systems 都是獨立、完整的集成開發環境 (IDE),可用於簡化 Power 服務器上的移植和開發,是開發人員在移植到 Linux on Power 時可以使用的優秀技術工具。

IBM SDK for PowerLinux with Migration Advisor

IBM Software Development Kit for PowerLinux (SDK) 是一個免費、基於 Eclipse 的 IDE。該 SDK 集成了 C/C++ 源代碼開發與 Advance Toolchain、鏈接後優化工具(比如 FDPR)和經典的 LInux 性能分析工具(包括 OProfile、Perf 和 Valgrind)。此外,IBM SDK for PowerLinux 中有一個叫做 Migration Advisor 的工具。Migration Advisor 工具的一個特性是代碼檢查器代碼修復 特性,該工具可以分析代碼中的 Linux/x86 漏洞,爲您提供快速修復代碼中的漏洞的選項或者提供手動修復它的建議。例如,下面的轉換是從 x86 Linux 移植到 Linux on Power 時一種典型的字節順序潛在問題。消息 “Cast with endianness issue checker” 表示存在這樣的漏洞。

void foo() {
    short int val = 0xFF00;
    char *char_pointer = (char *) &val;     
    //This prints 0 in x86 (or x86_64) machines and ff in ppc (or ppc64) machines.
    printf("%x\n", *char_pointer);
}

Migration Advisor 使用 Eclipse CDT Code Analyzer (Codan) 源代碼分析器來查找 C/C++ 源代碼中的潛在遷移問題,比如可能在 x86 和 POWER 服務器上運行時生成不同結果的代碼塊。要查找源代碼中的問題,Codan 會分析 C/C++ 源代碼抽象語法樹 (AST),查找可能與 Power Architecture 不兼容的代碼段。在找到潛在問題時,Migration Advisor 會保存該問題在代碼中的位置,並在源代碼中的這個特定位置添加一條警告。目前,Migration Advisor 僅兼容 C/C++ 代碼。

以下是在 IBM SDK for PowerLinux 中使用 Migration Advisor 的屏幕截圖。

圖 2. 運行 Migration Advisor ‘Union with Endianness Issues’ 檢查器
運行 Migration Advisor ‘Union with Endianness Issues’ 檢查器

Migration Advisor:快速修復特性

Migration Advisor 還爲一些遷移問題提供了快速修復方法,能夠將依賴於架構的代碼塊替換爲與 POWER 處理器兼容的指令。可以通過兩種方式觸發快速修復:右鍵單擊源代碼中的警告並選擇快速修復選項;或者打開 Migration Advisor Eclipse View,右鍵單擊一個特定問題,然後選擇快速修復選項。

Migration Advisor 所提供的快速修復僅適用於:

  • 特定於 x86、內置的編譯器的使用
  • 內聯程序集的使用
  • 性能降級

您可以選擇可激活或可停用的檢查器。每次您修改源代碼後,Migration Advisor 都將重新檢查代碼並更新結果,使您在對項目執行完成的重新構建之前有機會快速識別和解決問題。圖 3 顯示了 Migration Advisor 窗口,其中已激活所有檢查器。

圖 3. 激活所有檢查器
激活所有檢查器

激活 Migration Advisor 檢查其後,右鍵單擊項目並單擊 Run Migration Advisor。然後,Migration Advisor 會使用啓用的檢查器分析代碼,並在 Eclipse 視圖中顯示找到的潛在問題的結果(參見圖 4)。

圖 4. Migration Advisor 視圖
Migration Advisor 視圖

圖 5 顯示了一個不受 Power 平臺支持的內置插件所導致的一個真實遷移問題的示例。爲了修復該問題,可以在 Migration Advisor Eclipse 視圖中雙擊它。然後將打開源代碼視圖,突出顯示問題的準確位置。如果將鼠標移動到突出顯示的代碼上方並按 Ctrl+1,則會打開另一個彈出窗口,如圖 5 所示。

圖 5. 使用 Migration Advisor 進行快速修復
使用 Migration Advisor 進行快速修復

選擇第一個選項 Builtin quick fix。應用快速修復後的結果代碼如圖 6 所示。

圖 6. 應用 Migration Advisor 快速修復後的代碼
應用 Migration Advisor 快速修復後的代碼

其他有用的 Migration Advisor 檢查包括:

  • 特定於 x86 的程序集檢查器
  • 包含位字段的結構的檢查器
  • Long double 使用檢查器
  • 性能降級檢查器
  • 特定於 x86 的編譯器內置檢查器的使用

關於這些方便的檢查的完整列表,請參閱 IBM 知識中心

Linux Cross-platform Tool (LCT)
SDK for PowerLinux 還提供了另一個可以在任何現有的 Intel x86 平臺上運行的方便工具。它是一個命令行工具,用於識別應用程序的潛在可移植性。這樣,在移植到 Linux on Power 之前,您能夠快速瞭解源代碼中的潛在問題。在安裝 IBM SDK for PowerLinux 後,可以在該工具的 README 部分中找到 LCT 文檔。

IBM Rational Developer for Power Systems
IBM Rational Developer for Power Systems 也提供了一個富桌面集成開發、移植和優化環境,用於多平臺開發(IBM i、AIX 或 Linux on Power)。它的特性還包括各種移植工具,比如 Code Analysis 工具、Performance Advisor 工具和 Migration Assistant,類似於 IBM SDK for PowerLinux,這些工具可以檢測 32 位到 64 位遷移問題、字節順序問題,以及其他可能阻礙移植的操作系統問題。

JVM
Java 虛擬機 (JVM) 在所有平臺上都以高位優先模式運行,因此常常對處理器架構帶來的影響免疫。

數據類型和對齊方式

應用程序二進制接口 (ABI) 中重要的 有符號/無符號字符 細微差異。

在 x86/x86_64 下,“char” 的默認值爲 “signed char”,而 Power Systems 上的默認值爲 “unsigned char”。在 Power Systems 上,可以使用 GCC 指令 -fsigned-char 覆蓋該默認值。等效的 IBM XL 指令爲 -qchars=signed

Linux 操作系統上的 GCC 和 XL C/C++ 編譯器都提供了兩種不同的編程模型:ILP32 和 LP64。ILP32 表示 Integer Long Pointer 32,是 Linux 上原生的 32 位編程環境。ILP32 數據模型提供了一個 32 位地址空間,它的理論內存被限制爲 4 GB。LP64 表示 Long Pointer 64,是 Linux 上的 64 位編程環境。

表 1 顯示了 POWER 和 x86 平臺上的 ILP32 和 LP64 模型中的基礎數據類型中的位寬。

表 1. POWER 和 x86 平臺上的 ILP32 和 LP64 模型中的基礎數據類型(位)

基礎數據類型 POWER x86
  ILP32 ILP64 ILP32 ILP64
char
默認值:x86 上爲 signed - POWER 上爲 unsigned
8 8 8 8
Short 16 16 16 16
Int 32 32 32 32
Float 32 32 32 32
Long 32 64 32 64
Long long 64 64 64 64
Double 64 64 64 64
Long double 64/128* 64/128* 96 128
指針 32 64 32 64

*Linux on Power 上的 long double 的默認大小現在爲 128 位。如果使用編譯器選項 -qnoldb128 和 XL C/C++ 編譯器,那麼它們可以減少到 64 位。

在 Power 和 x86 平臺上的 /usr/include/limits.h 中,可以找到數字值的所有定義。

許多遺留 Linux x86 應用程序在 32 位下運行。對於最新的 x86 架構(支持並鼓勵採用 64 位應用程序),更多的 x86 應用程序是在 64 位模式下原生地更新或編寫的。在將 x86 應用程序移植到 Power Systems 的練習中,可採用 Linux on Power 環境作爲目標來匹配您的來源環境。換句話說,我們推薦首先完成初始移植,然後再考慮移植到 64 位編程模型。如果希望將 32 位 x86 應用程序移植到 64 位 Power Systems 編程模型,那麼可以將遷移分爲兩步:

  1. 移植到 Linux on Power 32 位環境(包括測試和驗證它)。
  2. 然後遷移到 64 位環境。

如果應用程序滿足以下條件:那麼工程師應考慮將應用程序移植到 64 位:

  • 能從超過 4GB 的虛擬地址空間受益
  • 能夠從更多物理內存(大於 4GB)受益,以及用戶可能將它部署在具有超過 4GB 物理內存的系統上
  • 能夠從 64 位大小的長整數受益
  • 能夠使用完整的 64 位寄存器來執行高效的 64 位算法
  • 使用大於 2GB 的文件

一些可通過遷移到 64 位而受益的應用程序示例包括:

  • 數據庫應用程序,尤其是執行數據挖掘的應用程序
  • Web 緩存和 Web 搜索引擎
  • 計算機輔助設計 (CAD)/計算機輔助工程 (CAE) 模擬和建模工具的組件
  • 科學和技術計算應用程序,比如計算流體動力學、遺傳模擬等

應用程序可保留 32 位並仍在 64 位 Linux on Power 內核上運行,無需任何代碼更改。基於 IBM Power 處理器的服務器支持 32 位和 64 位應用程序同時在 64 位架構上運行

在不同平臺(從 x86 到 Power)或編程模型(從 ILP32 到 LP64)之間移植應用程序時,需要考慮不同環境中提供的數據寬度和對齊設置之間的差異,以避免可能的性能降級和數據損壞。

在從 x86 ILP32 移植到 POWER ILP32 或從 x86 LP64 移植到 POWER LP64 時,您可能注意到,在表 1 中,所有基本數據類型的寬度都保持相同,除了 long double 96 位變爲 IPL32 上的 64/128 位,128 位變爲 LP64 上的 64/128 位。這意味着您應該檢查與 long double 數據類型相關的代碼部分。如果計劃使用 XL C/C++ 編譯器,那麼可以使用 -qlongdouble 編譯標誌在 long double 數據類型上實現最高的兼容性。

數據對齊

在平臺之間或 32 位與 64 位模型之間移植應用程序時,需要考慮不同環境中提供的對齊設置之間的差異,以避免可能的性能降級和數據損壞。最佳實踐是對數據項執行自然對齊。自然對齊表示將數據項存儲在是其大小的倍數的地址上(例如,8 字節數據項存儲在一個爲 8 的倍數的地址中)。對於 XL C/C++ 編譯器,依據 linuxppc 或位包裝規則,聚合中的每種數據類型(C/C++ 結構/union 和 C++ 類)將與字節邊界對齊,其中 linuxppc 是默認規則,這是一種自然對齊方式。linuxppc 也兼容默認的 GCC 對齊規則。表 2 顯示了 POWER 和 x86 上的對齊規則,以及它們的數據類型寬度(以字節爲單位)。

表 2. POWER 和 x86 上的對齊值(以字節爲單位)

 數據類型 POWER x86
  ILP32 ILP64 ILP32 ILP64
  寬度 對齊值 寬度 對齊值 寬度 對齊值 寬度 對齊值
Char 1 1 1 1 1 1 1 1
Short 2 2 2 2 2 2 2 2
Int 4 4 4 4 4 4 4 4
Float 4 4 4 4 4 4 4 4
Long 4 4 8 8 4 4 8 8
Long long 8 8 8 8 8 8 8 8
Double 8 8 8 8 8 8 8 8
Long double 8/16 8 8/16 8 12 4 16 16
指針 4 4 8 8 4 4 8 8

GCC 和 XL C/C++ 中的關鍵字 __alignof__ 允許您查詢對象的對齊方式。它的語法與 sizeof 類似。例如,如果目標系統需要將一個 double 值與 8 字節邊界對齊,那麼 __alignof__ (double) 爲 8。

如表 2 中所示,long double 變量在 x86 上與 4 字節對齊,在 Power Systems 上與 8 字節對齊。因此,不同平臺上的結構將具有不同的佈局。不要硬編碼任何大小和偏移,這很重要。相反,您應該使用 C 運算符 sizeof 來查詢基礎和複雜類型的大小。宏 offsetof 可用於從結構開頭獲取結構成員的偏移。

確定要使用哪個編譯器:GCC 或 IBM XL C/C++

有兩個 C/C++ 編譯器可用於 Linux on Power:GCC 和 IBM XL C/C++ 編譯器。GCC 針對 Linux 上的編譯提供了可靠的代碼移植功能,而在使用更高級的優化時,與 GCC 相比,IBM XL 編譯器顯著提升了性能。兩種編譯器都提供了 32 位和 64 位編譯模式,Linux on Power 環境支持在不降低性能的情況下同時運行 32 位和 64 位代碼。

使用 GCC 編譯器集進行移植

如果項目跨多個平臺而開發,而且其中 GCC 編譯器是原始的編譯器,那麼 GCC 編譯器常常用於部署針對 Linux on Power 的應用程序。性能不那麼關鍵的應用程序(比如小型實用程序)通常屬於這種情況。GCC 還允許使用一些只有 GCC 能夠理解的代碼原型,比如特定於 GCC 的宏。但要注意的是,許多特定於 GCC 的特性都合併在 XL C/C++ 編譯器中。

一般而言,使用 GCC 移植代碼應該很簡單。在大部分情況下,只需簡單地重新編譯和鍵入 make 命令即可。架構可能有所不同,而且有時可能存在庫版本差異。但從很大程度上講,它在哪個架構上運行並不重要。我們不鼓勵您在 Power Linux 上編譯特定於架構的標誌,比如 -m486-mpowerpc64,因爲 GCC 沒有針對這些架構上的優化例程的豐富的處理器映射。同樣地,如果不使用特定於架構的標誌,那麼不同 POWER 硬件模型上的二進制兼容性會更高。

SLES 11 和 RHEL 6 中包含的 GCC 編譯器同時支持 32 位和 64 位應用程序。在= SLES 11 和 RHEL 6 版中,默認編譯模式爲 64 位。

在所有架構上,庫必須使用 -fPIC 來編譯;x86 默認情況下會在 GCC 中應用此標誌。在 POWER 上,此標誌指定將生成的代碼用在一個共享對象中。請查閱 GCC 編譯器手冊,以便了解有關的更多信息(參見 “參考資料”)。

使用 IBM XL C/C++ 編譯器進行移植

XL C/C++ 使用了 GNU C 和 C++ 頭文件,而且結果應用程序被鏈接到 GCC 提供的 C 和 C++ 運行時庫。這意味着 XL C/C++ 編譯器生成了 GNU Executable and Linking Format (ELF) 對象,這些對象完全兼容 GCC 編譯器生成的對象。XL C/C++ 包含對稱多處理 (SMP) 運行時庫,以便支持 XL C/C++ 的自動並行化和 OpenMP 特性。

從 GCC 移植到針對 Linux on Power 的 XL C/C++ 很簡單。XL C/C++ 通過提供選項 -qinfo=por 來幫助完成此任務,幫助您過濾掉省略的診斷消息,進現實與可移植性問題相關的消息。此外,XL C/C++ 支持 gcc 和 gcc-c++ 的 GNU 擴展的一個子集。

請參閱 “XL C/C++ for Linux on pSeries 編譯器參考”,瞭解受支持的特性的完整列表,以及那些被接受但擁有忽略了的語義的特性。

要對 C 代碼使用受支持的特性,可以指定 -qlanglvl=extended-qlanglvl=extc89。在 C++ 中,默認情況下會接受所有受支持的 GNU gcc/gcc-c++ 特性。此外,gxlc 和 gxlc++ 有助於最小化對使用 GCC 編譯器構建的現有應用程序的 makefile 的更改

XL C/C++ 中的優化選項

XL C/C++ 提供了一些專門針對 IBM 硬件的優化選項。對於 Linux on Power,與使用 GCC 編譯的應用程序相比,許多使用 XL C/C++ 並利用正確的優化標誌組合而編譯的應用程序都有巨大的性能改進。請注意,不是所有優化對所有應用程序都有利。通常,您需要在編譯器完成的優化程度與編譯時間的增加和調試能力的降低之間進行權衡。

優化級別
優化級別由編譯器選項來指定。下表總結了每個優化級別上的編譯器行爲。

表 3. 每個優化級別上的編譯器行爲

選項 行爲
-qnoopt 提供快速編譯和完全調試支持。
-O2(與 -O 相同) 執行被編譯器開發人員視爲編譯速度和運行時性能的最佳組合的優化。此設置會暗中使用 -qstrict-qstrict_induction,除非使用
-qnostrict_induction-qnostrict 進行明確否定。
-O3 執行更多內存密集型和/或編譯時間密集型的優化。在運行時改進比最小化編譯資源的利用更重要時,推薦執行這些優化。
-O4 和 -O5 執行過程間優化、循環優化和自動機器調優。

應避免的舊 Power 選項

從 x86 移植到 Linux on Power 時,來自舊 Power Architecture 的一些選項應避免使用且有害

  • 使用 -mminimal-toc-mfull-toc 的 PPC64 構建版本應替換爲 -mcmodel=medium(這是默認值)
  • -ffloat-store,不會在 Power 上使用

目標機器選項告知編譯器生成能夠在給定微處理器或架構家族上最優地執行的代碼。通過選擇合適的目標機器選項,可通過優化來適應最廣泛的目標處理器、給定處理器架構家族的許多處理器,或者一個特定的處理器。以下選項會控制影響目標機器的各個方面的優化:

表 4. 影響目標機器的各個方面的優化選項

選項 行爲
-qarch 選擇應針對其生成指令代碼的一個處理器架構家族。默認值爲 -qarch=auto。還提供了以下子選項:ppc64grsq、pwr3、pwr4、pwr5、ppc970、ppc64、ppcgr、rs64b 和 rs64c。
-qtune 針對在給定微處理器上執行而進行的基礎優化,沒有暗示要用作目標的指令集架構的任何信息。Linux 上的默認值爲 -qtune=auto。可用的子選項包括:pwr3、pwr4、pwr5、pwr6、pwr7、pwr8、ppc970、rs64b 和 rs64c。但是,在許多情況下,此設置會基於 –qarch 的設置而自動設置。
-qcache 定義一種特定的緩存或內存幾何結構。如果使用了 -qcache,則與它一起使用 -qhot-qsmp
-qhot 高階轉換屬於專門通過交換 (interchange)、熔合 (fusion) 和展開 (unroll) 等技術來改善循環性能的優化。指定 -qhot 時,-qhot=vector 是默認選項。您可以嘗試將 -qhot -O2-O3 結合使用。它的設計目標是在沒有轉換機會時產生一種中立效果。
-qsmp 生成共享內存並行處理所需的多線程代碼。指定 -qsmp 時,-qsmp=auto 是默認選項。如果您在編譯一個 OpenMP 程序且不想要自動並行化,那麼可以使用 -qsmp=omp:noauto。使用 -qsmp 時始終使用 _r 編譯器調用。

要最充分地利用目標機器選項,您應該:

  • 使用 -qarch,指定您希望代碼能在其上良好運行的最小的機器系列。
  • 使用 -qtune,指定應具有最佳性能的機器。例如,如果將在基於 POWER8 處理器的系統及更高版本的系統的 Power 服務器上運行應用程序,那麼可以使用 -O3 -qarch=pwr8 -qtune=pwr8

IBM Power 平臺支持其他平臺上所沒有的機器指令。XL C/C++ 提供了一組內置的函數,它們直接對應於某些 POWER 處理器指令。使用這些函數能消除函數調用-返回成本、參數傳遞、堆棧調整和所有其他與函數調用相關的成本。有關支持的內置函數的完整列表,請參閱 XL C/C++ C++ for Linux on pSeries 編譯器參考 安裝文檔。

但是,在使用 IBM XL C/C++ 編譯器重新編譯時,最初打算使用 GCC 編譯器編譯的軟件可能需要更多地進行關注。您需要編輯 makefile 來反映 XL C/C++ 編譯器的正確路徑,該路徑默認爲 /opt/ibmcmp/。還需要設置針對特定架構的正確的優化標誌(比如 -03 -qarch=pwr8 -qtune=pwr8 用於 IBM POWER8 處理器)。

除了針對各種 Power Architecture 衍生產品的優化模式,還可以告訴 XL C/C++ 編譯器在通用模式下編譯軟件。這可以確保跨所有 Power Architecture 衍生產品的兼容性,但會犧牲通過特定於架構的優化所獲得的性能優勢。在編譯 64 位代碼時,必須爲編譯器提供針對 64 位構建版本的標誌(即 -q64),因爲它在默認情況下爲 32 位模式。

下面列出了使用 XL C/C++ 編譯器編譯面向 GCC 的代碼的一些技巧:

  • 在默認情況下,GCC 允許在 C 文件中使用 C++ 風格的註釋,但這不適合 XL C 編譯器 XLC。因爲更改一組源文件中的所有註釋來遵守 C 風格的註釋格式不太合算,所以 XLC 提供了一個 -q 參數來允許使用這些註釋:-q cpluscmt。在使用此標誌編譯 C 代碼時,會解釋 C 和 C++ 風格的註釋。
  • 環境變量常常是配置構建腳本的最簡單的方式。無需手動編輯配置腳本和 makefile,您應該設置相關的環境變量,比如 $CC$CFLAGS,以便允許通過配置腳本來生成 makefile,而無需手動編輯。
  • 配置腳本還需要知道正確的平臺類型。平臺類型將爲 linux-powerpc-unknown-gnu 或 linux-powerpc64-unknown-gnu。您應該通過將 -target= 標誌被附加到配置腳本中,爲使用 GCC 或 XL C/C++ 的編譯設置此平臺類型。
  • 儘管 IBM XL C/C++ 編譯器有許多文檔,您仍然可以運行該編譯器而不使用參數在控制檯顯示參數列表,例如 $COMPILER_PATH/bin/cc

編譯器選項比較

下表比較了來自 GCC 和 XL C/C++ 的常用編譯器選項。

表 5. 來自 GCC 和 XL C/C++ 的常用編譯器選項

GCC XL C/C++ 描述
-v -v、-V、-# 打開詳細模式。
-p/-profile -p 設置編譯器生成的對象文件,以進行分析。
-m32、-m64 -q32、-q64,或者設置 OBJECT_MODE 環境變量 創建 32 或 64 位對象
-fsyntax-only -qsyntaxonly 執行語法檢查而不生成對象文件。
-fpic -qpic=small 生成與位置無關的代碼,以便在共享庫中使用它們。在 XL C/C++ 中,全局偏移表的大小不大於 64 KB。如果指定了 -qpic 而沒有任何子選項,則假設使用了 -qpic=small。如果指定了 -qmkshrobj 編譯器選項,則會啓用 -qpic 選項。
-fPIC -qpic=large 允許全局偏移表大於 64 KB。
-pthread -qthreaded 或 _r invocation 模式 創建在多線程環境中運行的程序。
-fno-rtti -qnortti 禁止生成運行時類型標識 (RTTI) -qrtti 來進行異常處理,並且禁止 typeiddynamic_cast 運算符使用 RTTI。在 XL C/C++ 上,默認選項爲 -qnortti
-static -qstaticlink 阻止此選項生成的對象與共享庫鏈接。
-static-libgcc -qstaticlink=libgcc 告訴編譯器與 libgcc 的靜態版本鏈接。
-shared -qmkshrobj 告訴編譯器生成一個共享對象。
-shared-libgcc -qnostaticlink=libgcc 告訴編譯器與 libgcc 的共享版本鏈接。
-Wl、-rpath -Wl、-rpath 或 -R 傳遞一個冒號分隔的目錄列表,用它來指定一個運行時鏈接所搜索的目錄。
-fno-implicit-templates、-frepo -qtempinc、-qtemplateregistry、-qtemplaterecompile 實例化模板。
-w -w 禁止警告消息。
  -warn64 啓用對長整型到整型的截斷操作的檢查。
  -qinfo=< > 生成包含信息的消息。
-fpack-struct -qalign=bit_packed 使用 bit_packed 對齊規則。
  -qalign=linuxppc 使用默認 GCC 對齊規則保持與 GCC 對象的兼容性。這是默認設置。
-O、-O2、-O3 -O、-O2、-O3、-O4、-O5 用於設置優化級別。
Ofast、Og   Ofast – 提供速度優化,不完全符合標準
Og – 提供優化,但允許在需要時執行一些有效的調試。
-mcpu、-mtune -qarch、-qtune、-qcache 用於設置一個特定處理器的優化選項。

使用 GCC 構建大型程序

客戶有時需要構建一個在運行時生成的非常大的可執行文件。清單 5 中給出了一條常見的錯誤消息。

清單 5. 構建大型可執行文件時的錯誤消息

modelfile.cxx.o:(.text+0x212012):
       relocation truncated to fit: 
R_PPC64_TOC16_DS against `.toc'+10000

這是大型程序和舊 GCC 編譯器的一個常見問題。舊 GCC 編譯器樂觀地假設目錄 (table of contents, TOC) 不會溢出單個加載指令的 64 KB 範圍。在這種情況下,該程序很大,需要的所有 TOC 條目的總和超過 16 位 (TOC16_DS)。

有兩個解決方案。

  1. 對於 Advance Toolchain 3.0 和舊編譯器,可以使用 -mminimal-toc 進行重新編譯。
  2. 對於 Advance Toolchain 4.0 和更高版本的編譯器,可以使用 -mcmodel=medium 進行重新編譯。-mcmodel=medium 應具有更好的性能,但需要升級。

有關的更多細節,請參閱 GCC PowerPC 選項

移植步驟

完成每個計劃步驟後,應該已經準備爲執行移植做好了準備。本節將介紹成功將應用程序移植到 Linux on Power 的推薦步驟。

  1. 將構建系統遷移到 GNU Make(如果有必要)
    這是構建一個或多個 makefile 的過程。您還可以利用 GNU 的程序構建自動化實用程序,比如 Autoconf、Automake 和 Buildtool,以便最大程度地提高您的程序跨不同 UNIX® 平臺移植的能力。GNU 的程序構建自動化實用程序位於 directory.fsf.org/devel/build/。
  2. 修改依賴於架構的代碼(如果有必要)
    考慮字節順序、32 位和 64 位模型下的數據長度,以及不同平臺上的數據對齊方式,這些已經在 理解 x86 與 Power Architecture 衍生產品之間的差異 一節中介紹過。
  3. 構建
    構建 makefile 並修改了所有程序後,構建流程很簡單,只需發出一個命令,比如 make。如果在構建過程中遇到錯誤,這些錯誤通常是編譯器和鏈接器錯誤,以及程序語法錯誤。通過 makefile 修改編譯器選項或修改代碼中的引起問題的語法,通常可以修復錯誤。編譯器參考菜單和編程指南是此階段的最佳參考資料。對於 IBM XL C/C++,請參閱 XL C/C++ for Linux 編譯器參考 (compiler.pdf) 和 XL C/C++ for Linux 編程指南 (proguide.pdf)。對於 GCC,編譯藏靠和編程指南都在 gcc.gnu.org/onlinedocs 上。
  4. 測試和問題排除
    成功構建程序後,測試其中是否有運行時錯誤。運行時錯誤通常與此階段的程序邏輯相關。編寫多個測試程序來驗證應用程序的輸出是否滿足預期,總是一種不錯的想法。
  5. 調優性能
    移植的代碼已在 Power 平臺上運行後,可監視它以確保它按預期執行。如果不符合預期,您需要完成性能調優。可使用以下工具套件識別應用程序中的性能問題,顯示應用程序與 Linux 內核的交互情況。
    • OProfile
      OProfile 工具分析基於硬件相關事件的代碼,比如緩存遺漏理器週期。例如,OProfile 可幫助確定導致大部分緩存遺漏錯誤的來源例程。OProfile 使用了許多處理器中提供的硬件性能計數器,包括 IBM POWER6 和 POWER7。有關 OProfile for Power Linux 的更多信息,請訪問 OProfile 網站(參見 參考資料)。
    • 鏈接後優化 - 也稱爲 FDPRpro
      鏈接後優化工具通過在程序用於執行一些典型工作負載時收集程序的行爲信息,優化該程序的可執行鏡像。然後,它會重新分析該程序(結合收集的概要文件),應用全局優化(包括程序重構),以及創建一個針對該工作負載而優化的新程序版本。該優化器生成的新程序通常比原始程序運行得更快,使用的真實內存更少。有關更多信息,請訪問鏈接後優化網站(參見 參考資料)。

  6. 如果移植的應用程序將是一個商業產品,或者想要將該應用程序分發給第三方來安裝,那麼您需要打包移植的應用程序,包含庫、文檔,有時還包含源代碼。Linux 提供了多種方式來打包應用程序,比如 .tar 文件、自安裝的 shell 腳本,以及 RPM。RPM 是用於 Linux 的最流行的打包工具之一。

圖 7. 上述 1 至 6 步中描述的移植活動流

結束語

Linux on Power 提供了一個企業級 Linux 環境,其中同時包含 32 位和 64 位應用程序環境和工具鏈。Linux on Power 提供了兩組編譯器,它們簡化了開源代碼的移植和對屢獲殊榮的 Power Architecture 的高性能的利用。通過將 Linux on x86 應用程序移植到 Linux on Power,您可以利用 Power Architecture 上提供的應用程序性能,這一性能現在還得到了 Linux 操作系統上之前從未提供過的開發工具的增強。總之,Linux on Power 是一個部署高性能 Linux 應用程序的領先平臺。

致謝

感謝 Calvin Sze 提供原始文章,感謝 Linda Kinnunen 提供原始文章模板和很有幫助的評審,感謝 IBM Linux on Power 團隊提供技術幫助和審閱本文。

感謝 Otavio Busatto Pontes 針對新的 IBM SDK for Linux on Power 而更新了本文。訪問 Linux on Power 社區 瞭解針對該 SDK 的更多鏈接,以及來自 Wainer dos Santos Moschetta 的有關該 SDK 的博客文章。

感謝 Roberto Guimaraes Dutra de Oliveira/Brazil/IBM@IBMBR 和 Leonardo Rangel Augusto/Brazil/IBM@IBMBR 更新 IBM SDK for PowerLinux 中的 Migration Advisor。

參考資料

相關信息

推薦的工具和文檔


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章