安装并使用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、AutoFDOCoreBOLT等技术可以在不同程度上优化程序性能。在支持不同架构(X86、Arm64)基础上,进一步针对倚天710芯片进行优化,取得额外的性能提升。

  • Alibaba Cloud Compiler提供了良好的Coroutine(协程)和Modules(模块)支持,提供了模块化的标准库。为了方便开发者,阿里云还提供了C++基础库yaLanTingLibs,包括Coroutine(协程)库、序列化、RPCHTTPC++开发者常用的组件。

    说明

    yaLanTingLibs是一个现代C++基础工具库的集合,主要为C++开发者提供高性能且极为易用的现代C++基础工具库,帮助您构建高性能的现代C++应用。

前提条件

已创建Alibaba Cloud Linux 3操作系统的ECS实例。具体操作,请参见自定义购买实例

说明

仅支持在Alibaba Cloud Linux 3上使用Alibaba Cloud Compiler,不支持在Alibaba Cloud Linux 2上使用。

Alibaba Cloud Compiler编译

  1. 执行以下命令,安装Alibaba Cloud Compiler。

    sudo yum install -y alibaba-cloud-compiler
  2. 执行以下命令,安装 libstdc++-devel

    libstdc++-devel提供了GNU标准C++库的头文件。

    sudo yum -y install libstdc++-devel
  3. 执行以下命令,导入环境变量。

    export PATH=/opt/alibaba-cloud-compiler/bin:$PATH
  4. 使用Alibaba Cloud Compiler编译示例如下。

    简易编译示例

    1. 执行以下命令,创建hello.cpp文件。

    2. sudo vim hello.cpp
    3. i键进入编辑模式,复制并粘贴以下内容。

    4. #include <iostream>
      int main() {
        std::cout << "hello C++" << std::endl;
        return 0;
      }
    5. 按 Esc 键,输入:wq后按Enter键,保存配置。

    6. 执行以下命令,编译模块化C++代码。

    7. clang++ -O2 hello.cpp -o hello.cpp.out
    8. 执行以下命令,执行程序。

      ./hello.cpp.out

      结果如下图所示,表明程序执行成功。

      image

    使用C++20 Coroutine、Modules编译

    Alibaba Cloud Compiler支持C++20Coroutine(协程)、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

    使用保证对齐的内存分配函数分配协程桢。

    编译示例:

    1. 执行以下命令,创建Hello.cppm文件。

      sudo vim Hello.cppm
    2. i键进入编辑模式,复制并粘贴以下内容。

      module;
      #include <iostream>
      export module Hello;
      export void hello() {
        std::cout << "Hello World!\n";
      }
    3. 按 Esc 键,输入:wq后按Enter键,保存配置。

    4. 执行以下命令,创建use.cpp文件。

      sudo vim use.cpp
    5. i键进入编辑模式,复制并粘贴以下内容。

      import Hello;
      int main() {
        hello();
        return 0;
      }
    6. 按 Esc 键,输入:wq后按Enter键,保存配置。

    7. 执行以下命令,编译模块化C++代码。

      # 预编译模块接口生成Hello.cppm文件。
      clang++ -std=c++20 Hello.cppm --precompile -o Hello.pcm
      # 编译主程序并链接模块生成可执行的Hello.out文件。
      clang++ -std=c++20 use.cpp -fprebuilt-module-path=. Hello.pcm -o Hello.out
    8. 执行以下命令,运行程序。

      ./Hello.out

      结果如下图所示,表明程序执行成功。

      image

    (可选)使用C++基础库yaLanTingLibs

    您可以选择使用C++基础库yaLanTingLibs,它基于C++20的协程特性和C++模板元编程功能,提供了协程库、序列化库、RPC库、HTTP等功能。您可以访问yalantinglibsasync_simple获取yaLanTingLibs更多的详细信息。

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

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

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

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

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

      使用yaLanTingLibs序列化库

      1. 执行以下命令,创建test.cpp文件。

        sudo vim test.cpp
      2. 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;
        }
      3. 按 Esc 键,输入:wq后按Enter键,保存配置。

      4. 执行以下命令,编译程序。

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

        ./test

        结果如下图所示,表明程序执行成功。

        image

      使用C++基础库RPC

      1. 执行以下命令,在服务端新建代码文件server.cpp

        sudo vim server.cpp
      2. 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();
        }
      3. 按 Esc 键,输入:wq后按Enter键,保存配置。

      4. 执行以下命令,在客户端新建代码文件client.cpp

        sudo vim client.cpp
      5. 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;
        }
      6. 按 Esc 键,输入:wq后按Enter键,保存配置。

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

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

        说明

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

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

        ./server & ./client

        结果如下所示。这些日志描述了服务器从启动监听、客户端发送请求、服务器处理请求以及客户端关闭连接的全过程。

        image

      使用C++基础库HTTP

      1. 执行以下命令,在服务端新建代码文件http.cpp

        sudo vim http.cpp
      2. 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());
        }
      3. 按 Esc 键,输入:wq后按Enter键,保存配置。

      4. 执行以下命令,编译HTTP程序。

        clang++ http.cpp -I /usr/include/ylt/thirdparty -std=c++20 -o http -lpthread
      5. 执行以下命令,执行HTTP程序。

        ./http

        结果如下图所示,表明程序执行成功。

        image