返回介绍

2.6 为 Eigen 库使能向量化

发布于 2025-05-06 21:45:54 字数 4685 浏览 0 评论 0 收藏 0

NOTE : 此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-02/recipe-06 中找到,包含一个 C++示例。该示例在 CMake 3.5 版(或更高版本) 中是有效的,并且已经在 GNU/Linux、macOS 和 Windows 上进行过测试。

处理器的向量功能,可以提高代码的性能。对于某些类型的运算来说尤为甚之,例如:线性代数。本示例将展示如何使能矢量化,以便使用线性代数的 Eigen C++库加速可执行文件。

准备工作

我们用 Eigen C++模板库,用来进行线性代数计算,并展示如何设置编译器标志来启用向量化。这个示例的源代码 linear-algebra.cpp 文件:

#include <chrono>
#include <iostream>
​
#include <Eigen/Dense>
​
EIGEN_DONT_INLINE
double simple_function(Eigen::VectorXd &va, Eigen::VectorXd &vb)
{
  // this simple function computes the dot product of two vectors
  // of course it could be expressed more compactly
  double d = va.dot(vb);
  return d;
}
​
int main()
{
  int len = 1000000;
  int num_repetitions = 100;
​
  // generate two random vectors
  Eigen::VectorXd va = Eigen::VectorXd::Random(len);
  Eigen::VectorXd vb = Eigen::VectorXd::Random(len);
​
  double result;
  auto start = std::chrono::system_clock::now();
  for (auto i = 0; i < num_repetitions; i++)
  {
    result = simple_function(va, vb);
  }
  auto end = std::chrono::system_clock::now();
  auto elapsed_seconds = end - start;
​
  std::cout << "result: " << result << std::endl;
  std::cout << "elapsed seconds: " << elapsed_seconds.count() << std::endl;
}

我们期望向量化可以加快 simple_function 中的点积操作。

如何实施

根据 Eigen 库的文档,设置适当的编译器标志就足以生成向量化的代码。让我们看看 CMakeLists.txt :

  1. 声明一个 C++11 项目:
    cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
    ​
    project(recipe-06 LANGUAGES CXX)
    ​
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_EXTENSIONS OFF)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
  2. 使用 Eigen 库,我们需要在系统上找到它的头文件:
    find_package(Eigen3 3.3 REQUIRED CONFIG)
  3. CheckCXXCompilerFlag.cmake 标准模块文件:
    include(CheckCXXCompilerFlag)
  4. 检查 -march=native 编译器标志是否工作:
    check_cxx_compiler_flag("-march=native" _march_native_works)
  5. 另一个选项 -xHost 编译器标志也开启:
    check_cxx_compiler_flag("-xHost" _xhost_works)
  6. 设置了一个空变量 _CXX_FLAGS ,来保存刚才检查的两个编译器中找到的编译器标志。如果看到 _march_native_works ,我们将 _CXX_FLAGS 设置为 -march=native 。如果看到 _xhost_works ,我们将 _CXX_FLAGS 设置为 -xHost 。如果它们都不起作用, _CXX_FLAGS 将为空,并禁用矢量化:
    set(_CXX_FLAGS)
    if(_march_native_works)
        message(STATUS "Using processor's vector instructions (-march=native compiler flag set)")
        set(_CXX_FLAGS "-march=native")
    elseif(_xhost_works)
        message(STATUS "Using processor's vector instructions (-xHost compiler flag set)")
        set(_CXX_FLAGS "-xHost")
    else()
        message(STATUS "No suitable compiler flag found for vectorization")
    endif()
  7. 为了便于比较,我们还为未优化的版本定义了一个可执行目标,不使用优化标志:
    add_executable(linear-algebra-unoptimized linear-algebra.cpp)
    ​
    target_link_libraries(linear-algebra-unoptimized
      PRIVATE
          Eigen3::Eigen
      )
  8. 此外,我们定义了一个优化版本:
    add_executable(linear-algebra linear-algebra.cpp)
    ​
    target_compile_options(linear-algebra
      PRIVATE
          ${_CXX_FLAGS}
      )
    ​
    target_link_libraries(linear-algebra
      PRIVATE
          Eigen3::Eigen
      )
  9. 让我们比较一下这两个可执行文件 - 首先我们配置(在本例中, -march=native_works ):
    $ mkdir -p build
    $ cd build
    $ cmake ..
    ​
    ...
    -- Performing Test _march_native_works
    -- Performing Test _march_native_works - Success
    -- Performing Test _xhost_works
    -- Performing Test _xhost_works - Failed
    -- Using processor's vector instructions (-march=native compiler flag set)
    ...
  10. 最后,让我们编译可执行文件,并比较运行时间:
    $ cmake --build .
    $ ./linear-algebra-unoptimized
    ​
    result: -261.505
    elapsed seconds: 1.97964
    ​
    $ ./linear-algebra
    ​
    result: -261.505
    elapsed seconds: 1.05048

工作原理

大多数处理器提供向量指令集,代码可以利用这些特性,获得更高的性能。由于线性代数运算可以从 Eigen 库中获得很好的加速,所以在使用 Eigen 库时,就要考虑向量化。我们所要做的就是,指示编译器为我们检查处理器,并为当前体系结构生成本机指令。不同的编译器供应商会使用不同的标志来实现这一点:GNU 编译器使用 -march=native 标志来实现这一点,而 Intel 编译器使用 -xHost 标志。使用 CheckCXXCompilerFlag.cmake 模块提供的 check_cxx_compiler_flag 函数进行编译器标志的检查:

check_cxx_compiler_flag("-march=native" _march_native_works)

这个函数接受两个参数:

  • 第一个是要检查的编译器标志。
  • 第二个是用来存储检查结果(true 或 false) 的变量。如果检查为真,我们将工作标志添加到 _CXX_FLAGS 变量中,该变量将用于为可执行目标设置编译器标志。

更多信息

本示例可与前一示例相结合,可以使用 cmake_host_system_information 查询处理器功能。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。