# 2.6 为Eigen库使能向量化

**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`文件:

```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`查询处理器功能。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/2.0-chinese/2.6-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
