Alibaba Cloud Compiler是阿里雲打造的C++編譯器,基於Clang/LLVM-13社區開源版本開發,繼承開源版本支援的所有選項、參數,同時結合阿里雲基礎設施進行深度最佳化、補充特性,可以讓您獲得更好的C++編譯器體驗。本文主要介紹如何在Alibaba Cloud Linux 3作業系統中安裝並使用Alibaba Cloud Compiler編譯器,協助您快速構建高效能的C++應用。
背景資訊
Alibaba Cloud Compiler編譯器相比GCC,或其他Clang/LLVM版本在純粹編譯、構建速度上有很大的提升,有以下幾點:
針對Clang/LLVM編譯器本身進行PGO等最佳化技術調優,進一步提升Clang/LLVM編譯速度。相比GCC等其他編譯器,在構建大規模C++業務代碼時取得顯著加速。
相比GCC使用的GNU連結器,Clang/LLVM的連結器lld效能更好,對大型二進位檔案效果尤為明顯。
基於C++20 Module特性支援,我們將C++標準庫模組化形成std-module。業務代碼以較小成本接入,即可獲得編譯加速。
Alibaba Cloud Compiler利用Alibaba Cloud Compiler(LLVM) ThinLTO、AutoFDO和CoreBolt等技術可以在不同程度上最佳化程式效能。在支援不同架構(X86、Arm64)基礎上,進一步針對倚天710晶片進行最佳化,取得額外的效能提升。
Alibaba Cloud Compiler提供了良好的Coroutine(協程)和Modules(模組)支援,提供了模組化的標準庫。為了方便開發人員,阿里雲還提供了C++基礎庫yaLanTingLibs,包括Coroutine(協程)庫、序列化、RPC和HTTP等C++開發人員常用的組件。
說明yaLanTingLibs是一個現代C++基礎工具庫的集合,它包括struct_pack、struct_json、struct_xml、struct_yaml、struct_pb、easylog、coro_rpc、coro_io、coro_http和async_simple等功能,主要為C++開發人員提供高效能、極度易用的現代C++基礎工具庫,協助您構建高效能的現代C++應用。
前提條件
已建立Alibaba Cloud Linux 3作業系統的ECS執行個體。具體操作,請參見自訂購買執行個體。
僅支援在Alibaba Cloud Linux 3上使用Alibaba Cloud Compiler,不支援在Alibaba Cloud Linux 2上使用。
Alibaba Cloud Compiler編譯
遠端連線ECS執行個體。
具體操作,請參見通過密碼或密鑰認證登入Linux執行個體。
運行以下命令,安裝Alibaba Cloud Compiler。
sudo yum install -y alibaba-cloud-compiler
運行以下命令,匯入環境變數。
export PATH=/opt/alibaba-cloud-compiler/bin:$PATH
使用Alibaba Cloud Compiler編譯。
簡易編譯樣本
# C++程式如下 cat hello.cpp #include <iostream> int main() { std::cout << "hello C++" << std::endl; return 0; } #編譯上述hello.cpp程式 clang++ -O2 hello.cpp -o hello.cpp.out
使用C++20 Coroutine、Modules編譯
Alibaba Cloud Compiler支援C++20的Coroutine(協程)、Modules(模組)特性,協程和模組特性為C++開發人員提供了更高效的編碼方式和改進的編譯效能。協程樣本請參見使用C++基礎庫RPC庫和使用C++基礎庫HTTP庫。
說明Coroutines(協程)是一種編程概念,它允許函數執行到一定點時掛起(suspend),並在以後的某個時刻恢複(resume)。這與傳統的函數調用有很大不同,傳統函數一旦調用,就會一直運行到結束。協程提供了一種更為靈活的控制流程機制,簡化了非同步編程和產生器模式的實現。
在傳統的C++編程模式中,代碼被組織為標頭檔(
.h
/.hpp
)和源檔案(.cpp
),並且標頭檔中的聲明必須在每個使用它的源檔案中通過#include
預先處理指令包含進來。這種模式會導致編譯器多次解析同一個標頭檔,從而增加編譯時間。Modules(模組)是C++為了改善程式碼群組織和編譯效率而引入的一項重大變革。
調用clang++編譯C++程式時,您可以指定如下選項使用:
參數名稱
功能說明
-std=
指定C++特性,協程與Modules特性在
-std=c++20
後生效。--precompile
將Module Unit編譯為BMI檔案。
-fprebuilt-module-path
指定尋找BMI檔案的路徑。
-fstd-modules
指定當前為編譯std modules模式。
-fmodules-export-all
指定當前modules中所有聲明都標記為已
export
。-fmodules-export-macros
使modules能夠export宏。
-try-load-bmi-when-preprocessing
在預先處理時嘗試尋找BMI。
-fstd-module-path
指定std module的尋找路徑。
-fcoro-aligned-allocation
使用保證對齊的記憶體配置函數分配協程楨。
編譯樣本:
# Modules 代碼如下 cat Hello.cppm module; #include <iostream> export module Hello; export void hello() { std::cout << "Hello World!\n"; } cat use.cpp import Hello; int main() { hello(); return 0; } #編譯上述 Modules 範例 clang++ -std=c++20 Hello.cppm --precompile -o Hello.pcm clang++ -std=c++20 use.cpp -fprebuilt-module-path=. Hello.pcm -o Hello.out ./Hello.out Hello World!
(可選)使用C++基礎庫yaLanTingLibs
您可以選擇使用C++基礎庫yaLanTingLibs,它基於C++20的協程特性和C++模板元編程功能,提供了協程庫、序列化庫、RPC庫、HTTP等功能。您可以訪問yalantinglibs和async_simple擷取yaLanTingLibs更多的詳細資料。
序列化庫是指用於序列化和還原序列化資料的軟體庫。序列化是將資料結構或對象狀態轉換為可以儲存或傳輸的格式(儲存到檔案、記憶體緩衝區或通過網路發送)的過程。相對應的,還原序列化是將儲存或傳輸的格式恢複為原始的資料結構或對象狀態的過程。
RPC(遠端程序呼叫)庫是一個提供處理序間通訊的軟體庫,它允許C++程式執行跨網路的函數或方法,就像調用本地函數一樣。RPC庫可以屏蔽網路傳輸、序列化、還原序列化、路由等諸多細節,使得開發人員可以專註於實現商務邏輯。
HTTP超文字傳輸通訊協定 (HTTP)是一種用於分布式、協作式和超媒體資訊系統的應用程式層協議。coro_http是C++20協程實現的高效能易用的HTTP庫,包括HTTP服務端和HTTP用戶端,可以協助使用者快速開發HTTP應用。
運行以下命令,安裝基礎庫yaLanTingLibs。
sudo yum install -y yalantinglibs-devel
使用基礎庫yaLanTingLibs序列化、RPC和HTTP。
使用yaLanTingLibs序列化庫
建立代碼檔案test.cpp。代碼內容如下:
#include <iostream> #include "ylt/struct_pack.hpp" struct person { int age; std::string name; }; int main() { person tom{.age=20,.name="tom"}; auto buffer = struct_pack::serialize(tom); auto tom2 = struct_pack::deserialize<person>(buffer); std::cout<<"age: "<<tom2.value().age<<", name: "<<tom2.value().name<<std::endl; return 0; }
該段代碼實現了對person的序列化和還原序列化。
運行以下命令,編譯器。
clang++ test.cpp -std=c++20 -o test
運行以下命令,執行程式。
./test
程式應輸出:
age: 20, name: tom
使用C++基礎庫RPC庫
在服務端建立代碼檔案server.cpp。代碼內容如下:
#include "ylt/coro_rpc/coro_rpc_server.hpp" std::string ping(std::string ping) { return "Receive: " + ping + ". Return pong."; } int main() { coro_rpc::coro_rpc_server server{1 , 8801}; server.register_handler<ping>(); return server.start(); }
該段代碼在服務端啟動一個持續啟動並執行RPC伺服器
coro_rpc
,在8801連接埠上監聽傳入的RPC請求,並註冊了一個RPC函數ping
,用於響應RPC調用。在用戶端建立代碼檔案client.cpp。代碼內容如下:
#include <iostream> #include "ylt/coro_rpc/coro_rpc_client.hpp" std::string ping(std::string); async_simple::coro::Lazy<void> example(){ coro_rpc::coro_rpc_client client; auto ec = co_await client.connect("localhost","8801"); assert(!ec); auto ret = co_await client.call<ping>("ping"); std::cout << ret.value() << std::endl; co_return; }; int main(){ async_simple::coro::syncAwait(example()); return 0; }
該段代碼在用戶端啟動一個RPC用戶端,用於串連RPC伺服器並調用
ping
函數,並列印了RPC函數的返回結果。在服務端運行以下命令,編譯服務端程式。
clang++ server.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o server -lpthread
在用戶端運行以下命令,編譯用戶端程式。
說明編譯前,請確保用戶端已安裝Alibaba Cloud Compiler並匯入環境變數。具體操作,請參見Alibaba Cloud Compiler編譯。
clang++ client.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o client -lpthread
在服務端或用戶端運行以下命令,啟動服務端和用戶端。
./server & ./client
程式應該輸出類似於下面日誌的內容:
2024-02-02 16:47:11.496 INFO [11960] [coro_rpc_server.hpp:289] begin to listen 2024-02-02 16:47:11.496 INFO [11961] [coro_rpc_client.hpp:412] client_id 0 begin to connect 8801 2024-02-02 16:47:11.496 INFO [11960] [coro_rpc_server.hpp:318] listen port 8801 successfully 2024-02-02 16:47:11.497 INFO [11967] [coro_rpc_server.hpp:348] new client conn_id 1 coming 2024-02-02 16:47:11.497 INFO [11967] [router.hpp:293] route function name: ping Receive: ping. Return pong. 2024-02-02 16:47:11.497 INFO [11968] [coro_rpc_client.hpp:356] client_id 0 close 2024-02-02 16:47:11.497 INFO [11967] [coro_connection.hpp:166] connection 1 close: End of file
這些日誌描述了伺服器從啟動監聽、用戶端發送請求、伺服器處理請求以及用戶端關閉串連的全過程。
使用C++基礎庫HTTP庫
建立檔案http.cpp。代碼內容如下:
#include <iostream> #include "ylt/coro_http/coro_http_client.hpp" #include "ylt/coro_http/coro_http_server.hpp" using namespace std::chrono_literals; using namespace coro_http; async_simple::coro::Lazy<void> basic_usage() { coro_http_server server(1, 9001); server.set_http_handler<GET>( "/get", [](coro_http_request &req, coro_http_response &resp) { resp.set_status_and_content(status_type::ok, "ok"); }); server.async_start(); std::this_thread::sleep_for(300ms); coro_http_client client{}; auto result = co_await client.async_get("http://127.0.0.1:9001/get"); assert(result.status == 200); assert(result.resp_body == "ok"); for (auto [key, val] : result.resp_headers) { std::cout << key << ": " << val << "\n"; } } int main() { async_simple::coro::syncAwait(basic_usage()); }
該段代碼啟動一個HTTP伺服器,註冊了一個get的HTTP服務,在連接埠9001監聽並等待HTTP請求到來。然後建立了一個HTTP用戶端去請求get服務,最後得到HTTP服務返回的資料。
運行以下命令,編譯HTTP程式。
clang++ http.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o http -lpthread
運行以下命令,執行HTTP程式。
./http
程式應該輸出類似於下面的日誌內容:
2024-02-02 09:07:26.622 INFO [8098] [coro_http_server.hpp:444] begin to listen 2024-02-02 09:07:26.622 INFO [8098] [coro_http_server.hpp:472] listen port 9001 successfully 2024-02-02 09:07:26.923 DEBUG [8101] [coro_http_server.hpp:501] new connection comming, id: 1 Server: cinatra Content-Length: 2 Date: Fri, 02 Feb 2024 01:07:26 GMT 2024-02-02 09:07:26.923 INFO [8103] [coro_http_server.hpp:37] coro_http_server will quit 2024-02-02 09:07:26.923 INFO [8101] [coro_http_server.hpp:491] accept failed, error: Operation aborted. 2024-02-02 09:07:26.923 INFO [8103] [coro_http_server.hpp:112] wait for server's thread-pool finish all work. 2024-02-02 09:07:26.923 INFO [8103] [coro_http_server.hpp:115] server's thread-pool finished. 2024-02-02 09:07:26.923 INFO [8103] [coro_http_server.hpp:117] stop coro_http_server ok