安装并使用Alibaba Cloud Compiler

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编译

  1. 远程连接ECS实例。

    具体操作,请参见通过密码或密钥认证登录Linux实例

  2. 运行以下命令,安装Alibaba Cloud Compiler。

    sudo yum install -y alibaba-cloud-compiler
  3. 运行以下命令,导入环境变量。

    export PATH=/opt/alibaba-cloud-compiler/bin:$PATH
  4. 使用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等功能。您可以访问yalantinglibsasync_simple获取yaLanTingLibs更多的详细信息。

  • 序列化库是指用于序列化和反序列化数据的软件库。序列化是将数据结构或对象状态转换为可以存储或传输的格式(保存到文件、内存缓冲区或通过网络发送)的过程。相对应的,反序列化是将存储或传输的格式恢复为原始的数据结构或对象状态的过程。

  • RPC(远程过程调用)库是一个提供进程间通信的软件库,它允许C++程序执行跨网络的函数或方法,就像调用本地函数一样。RPC库可以屏蔽网络传输、序列化、反序列化、路由等诸多细节,使得开发者可以专注于实现业务逻辑。

  • HTTP超文本传输协议是一种用于分布式、协作式和超媒体信息系统的应用层协议。coro_http是C++20协程实现的高性能易用的HTTP库,包括HTTP服务端和HTTP客户端,可以帮助用户快速开发HTTP应用。

  1. 运行以下命令,安装基础库yaLanTingLibs。

    sudo yum install -y yalantinglibs-devel
  2. 使用基础库yaLanTingLibs序列化、RPC和HTTP。

    使用yaLanTingLibs序列化库

    1. 新建代码文件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的序列化和反序列化。

    2. 运行以下命令,编译程序。

      clang++ test.cpp -std=c++20 -o test
    3. 运行以下命令,执行程序。

      ./test

      程序应输出:

      age: 20, name: tom

    使用C++基础库RPC库

    1. 在服务端新建代码文件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调用。

    2. 在客户端新建代码文件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函数的返回结果。

    3. 在服务端运行以下命令,编译服务端程序。

      clang++ server.cpp  -I /usr/include/ylt/thirdparty -std=c++20  -o server -lpthread
    4. 在客户端运行以下命令,编译客户端程序。

      说明

      编译前,请确保客户端已安装Alibaba Cloud Compiler并导入环境变量。具体操作,请参见Alibaba Cloud Compiler编译

      clang++ client.cpp  -I /usr/include/ylt/thirdparty -std=c++20  -o client -lpthread
    5. 在服务端或客户端运行以下命令,启动服务端和客户端。

      ./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库

    1. 新建文件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服务返回的数据。

    2. 运行以下命令,编译HTTP程序。

      clang++ http.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o http -lpthread
    3. 运行以下命令,执行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