2.6 为Eigen库使能向量化

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

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

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

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

    1. 声明一个C++11项目:

      1. cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
      2. project(recipe-06 LANGUAGES CXX)
      3. set(CMAKE_CXX_STANDARD 11)
      4. set(CMAKE_CXX_EXTENSIONS OFF)
      5. set(CMAKE_CXX_STANDARD_REQUIRED ON)
      1. find_package(Eigen3 3.3 REQUIRED CONFIG)
    2. CheckCXXCompilerFlag.cmake标准模块文件:

    3. 检查-march=native编译器标志是否工作:

      1. check_cxx_compiler_flag("-march=native" _march_native_works)
    4. 设置了一个空变量_CXX_FLAGS,来保存刚才检查的两个编译器中找到的编译器标志。如果看到_march_native_works,我们将_CXX_FLAGS设置为。如果看到_xhost_works,我们将_CXX_FLAGS设置为-xHost。如果它们都不起作用,_CXX_FLAGS将为空,并禁用矢量化:

    5. 为了便于比较,我们还为未优化的版本定义了一个可执行目标,不使用优化标志:

      1. add_executable(linear-algebra-unoptimized linear-algebra.cpp)
      2. target_link_libraries(linear-algebra-unoptimized
      3. PRIVATE
      4. Eigen3::Eigen
      5. )
      1. add_executable(linear-algebra linear-algebra.cpp)
      2. target_compile_options(linear-algebra
      3. PRIVATE
      4. ${_CXX_FLAGS}
      5. )
      6. PRIVATE
      7. )
    6. 让我们比较一下这两个可执行文件——首先我们配置(在本例中,-march=native_works):

    7. 最后,让我们编译可执行文件,并比较运行时间:

      1. $ cmake --build .
      2. $ ./linear-algebra-unoptimized
      3. result: -261.505
      4. elapsed seconds: 1.97964
      5. $ ./linear-algebra
      6. result: -261.505
      7. 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变量中,该变量将用于为可执行目标设置编译器标志。