安裝并使用Alibaba Cloud Compiler
Alibaba Cloud Compiler是阿里云打造的C++編譯器,基于Clang/LLVM-13社區(qū)開源版本開發(fā),繼承開源版本支持的所有選項(xiàng)、參數(shù),同時(shí)結(jié)合阿里云基礎(chǔ)設(shè)施進(jìn)行深度優(yōu)化、補(bǔ)充特性,可以讓您獲得更好的C++編譯器體驗(yàn)。本文主要介紹如何在Alibaba Cloud Linux 3操作系統(tǒng)中安裝并使用Alibaba Cloud Compiler編譯器,幫助您快速構(gòu)建高性能的C++應(yīng)用。
背景信息
Alibaba Cloud Compiler編譯器相比GCC,或其他Clang/LLVM版本在純粹編譯、構(gòu)建速度上有很大的提升,有以下幾點(diǎn):
針對Clang/LLVM編譯器本身進(jìn)行PGO等優(yōu)化技術(shù)調(diào)優(yōu),進(jìn)一步提升Clang/LLVM編譯速度。相比GCC等其他編譯器,在構(gòu)建大規(guī)模C++業(yè)務(wù)代碼時(shí)取得顯著加速。
相比GCC使用的GNU鏈接器,Clang/LLVM的鏈接器lld性能更好,對大型二進(jìn)制文件效果尤為明顯。
基于C++20 Module特性支持,我們將C++標(biāo)準(zhǔn)庫模塊化形成std-module。業(yè)務(wù)代碼以較小成本接入,即可獲得編譯加速。
Alibaba Cloud Compiler利用Alibaba Cloud Compiler(LLVM) ThinLTO、AutoFDO和CoreBolt等技術(shù)可以在不同程度上優(yōu)化程序性能。在支持不同架構(gòu)(X86、Arm64)基礎(chǔ)上,進(jìn)一步針對倚天710芯片進(jìn)行優(yōu)化,取得額外的性能提升。
Alibaba Cloud Compiler提供了良好的Coroutine(協(xié)程)和Modules(模塊)支持,提供了模塊化的標(biāo)準(zhǔn)庫。為了方便開發(fā)者,阿里云還提供了C++基礎(chǔ)庫yaLanTingLibs,包括Coroutine(協(xié)程)庫、序列化、RPC和HTTP等C++開發(fā)者常用的組件。
說明yaLanTingLibs是一個(gè)現(xiàn)代C++基礎(chǔ)工具庫的集合,它包括struct_pack、struct_json、struct_xml、struct_yaml、struct_pb、easylog、coro_rpc、coro_io、coro_http和async_simple等功能,主要為C++開發(fā)者提供高性能、極度易用的現(xiàn)代C++基礎(chǔ)工具庫,幫助您構(gòu)建高性能的現(xiàn)代C++應(yīng)用。
前提條件
已創(chuàng)建Alibaba Cloud Linux 3操作系統(tǒng)的ECS實(shí)例。具體操作,請參見自定義購買實(shí)例。
僅支持在Alibaba Cloud Linux 3上使用Alibaba Cloud Compiler,不支持在Alibaba Cloud Linux 2上使用。
Alibaba Cloud Compiler編譯
遠(yuǎn)程連接ECS實(shí)例。
具體操作,請參見通過密碼或密鑰認(rèn)證登錄Linux實(shí)例。
運(yùn)行以下命令,安裝Alibaba Cloud Compiler。
sudo yum install -y alibaba-cloud-compiler
運(yùn)行以下命令,導(dǎo)入環(huán)境變量。
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(協(xié)程)、Modules(模塊)特性,協(xié)程和模塊特性為C++開發(fā)者提供了更高效的編碼方式和改進(jìn)的編譯性能。協(xié)程示例請參見使用C++基礎(chǔ)庫RPC庫和使用C++基礎(chǔ)庫HTTP庫。
說明Coroutines(協(xié)程)是一種編程概念,它允許函數(shù)執(zhí)行到一定點(diǎn)時(shí)掛起(suspend),并在以后的某個(gè)時(shí)刻恢復(fù)(resume)。這與傳統(tǒng)的函數(shù)調(diào)用有很大不同,傳統(tǒng)函數(shù)一旦調(diào)用,就會一直運(yùn)行到結(jié)束。協(xié)程提供了一種更為靈活的控制流機(jī)制,簡化了異步編程和生成器模式的實(shí)現(xiàn)。
在傳統(tǒng)的C++編程模式中,代碼被組織為頭文件(
.h
/.hpp
)和源文件(.cpp
),并且頭文件中的聲明必須在每個(gè)使用它的源文件中通過#include
預(yù)處理指令包含進(jìn)來。這種模式會導(dǎo)致編譯器多次解析同一個(gè)頭文件,從而增加編譯時(shí)間。Modules(模塊)是C++為了改善代碼組織和編譯效率而引入的一項(xiàng)重大變革。
調(diào)用clang++編譯C++程序時(shí),您可以指定如下選項(xiàng)使用:
參數(shù)名稱
功能說明
-std=
指定C++特性,協(xié)程與Modules特性在
-std=c++20
后生效。--precompile
將Module Unit編譯為BMI文件。
-fprebuilt-module-path
指定查找BMI文件的路徑。
-fstd-modules
指定當(dāng)前為編譯std modules模式。
-fmodules-export-all
指定當(dāng)前modules中所有聲明都標(biāo)記為已
export
。-fmodules-export-macros
使modules能夠export宏。
-try-load-bmi-when-preprocessing
在預(yù)處理時(shí)嘗試查找BMI。
-fstd-module-path
指定std module的查找路徑。
-fcoro-aligned-allocation
使用保證對齊的內(nèi)存分配函數(shù)分配協(xié)程楨。
編譯示例:
# 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++基礎(chǔ)庫yaLanTingLibs
您可以選擇使用C++基礎(chǔ)庫yaLanTingLibs,它基于C++20的協(xié)程特性和C++模板元編程功能,提供了協(xié)程庫、序列化庫、RPC庫、HTTP等功能。您可以訪問yalantinglibs和async_simple獲取yaLanTingLibs更多的詳細(xì)信息。
序列化庫是指用于序列化和反序列化數(shù)據(jù)的軟件庫。序列化是將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換為可以存儲或傳輸?shù)母袷剑ū4娴轿募?nèi)存緩沖區(qū)或通過網(wǎng)絡(luò)發(fā)送)的過程。相對應(yīng)的,反序列化是將存儲或傳輸?shù)母袷交謴?fù)為原始的數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)的過程。
RPC(遠(yuǎn)程過程調(diào)用)庫是一個(gè)提供進(jìn)程間通信的軟件庫,它允許C++程序執(zhí)行跨網(wǎng)絡(luò)的函數(shù)或方法,就像調(diào)用本地函數(shù)一樣。RPC庫可以屏蔽網(wǎng)絡(luò)傳輸、序列化、反序列化、路由等諸多細(xì)節(jié),使得開發(fā)者可以專注于實(shí)現(xiàn)業(yè)務(wù)邏輯。
HTTP超文本傳輸協(xié)議是一種用于分布式、協(xié)作式和超媒體信息系統(tǒng)的應(yīng)用層協(xié)議。coro_http是C++20協(xié)程實(shí)現(xiàn)的高性能易用的HTTP庫,包括HTTP服務(wù)端和HTTP客戶端,可以幫助用戶快速開發(fā)HTTP應(yīng)用。
運(yùn)行以下命令,安裝基礎(chǔ)庫yaLanTingLibs。
sudo yum install -y yalantinglibs-devel
使用基礎(chǔ)庫yaLanTingLibs序列化、RPC和HTTP。
使用yaLanTingLibs序列化庫
新建代碼文件test.cpp。代碼內(nèi)容如下:
#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; }
該段代碼實(shí)現(xiàn)了對person的序列化和反序列化。
運(yùn)行以下命令,編譯程序。
clang++ test.cpp -std=c++20 -o test
運(yùn)行以下命令,執(zhí)行程序。
./test
程序應(yīng)輸出:
age: 20, name: tom
使用C++基礎(chǔ)庫RPC庫
在服務(wù)端新建代碼文件server.cpp。代碼內(nèi)容如下:
#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(); }
該段代碼在服務(wù)端啟動(dòng)一個(gè)持續(xù)運(yùn)行的RPC服務(wù)器
coro_rpc
,在8801端口上監(jiān)聽傳入的RPC請求,并注冊了一個(gè)RPC函數(shù)ping
,用于響應(yīng)RPC調(diào)用。在客戶端新建代碼文件client.cpp。代碼內(nèi)容如下:
#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; }
該段代碼在客戶端啟動(dòng)一個(gè)RPC客戶端,用于連接RPC服務(wù)器并調(diào)用
ping
函數(shù),并打印了RPC函數(shù)的返回結(jié)果。在服務(wù)端運(yùn)行以下命令,編譯服務(wù)端程序。
clang++ server.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o server -lpthread
在客戶端運(yùn)行以下命令,編譯客戶端程序。
說明編譯前,請確保客戶端已安裝Alibaba Cloud Compiler并導(dǎo)入環(huán)境變量。具體操作,請參見Alibaba Cloud Compiler編譯。
clang++ client.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o client -lpthread
在服務(wù)端或客戶端運(yùn)行以下命令,啟動(dòng)服務(wù)端和客戶端。
./server & ./client
程序應(yīng)該輸出類似于下面日志的內(nèi)容:
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
這些日志描述了服務(wù)器從啟動(dòng)監(jiān)聽、客戶端發(fā)送請求、服務(wù)器處理請求以及客戶端關(guān)閉連接的全過程。
使用C++基礎(chǔ)庫HTTP庫
新建文件http.cpp。代碼內(nèi)容如下:
#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()); }
該段代碼啟動(dòng)一個(gè)HTTP服務(wù)器,注冊了一個(gè)get的HTTP服務(wù),在端口9001監(jiān)聽并等待HTTP請求到來。然后創(chuàng)建了一個(gè)HTTP客戶端去請求get服務(wù),最后得到HTTP服務(wù)返回的數(shù)據(jù)。
運(yùn)行以下命令,編譯HTTP程序。
clang++ http.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o http -lpthread
運(yùn)行以下命令,執(zhí)行HTTP程序。
./http
程序應(yīng)該輸出類似于下面的日志內(nèi)容:
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