在CMake中配置使用Intel One API并启用OpenMP(阶段2)

发布时间 2023-11-02 19:19:20作者: 崩溃侠

本示例尝试使用CMake配置使用Intel One API编译器编译支持OpenMP的程序。

环境

  • Visual Studio 2022 Community with C++ desktop
  • Intel One API 2023.2 with VS intergration
  • CMake 3.27.7

样例

程序代码

OpenMPDemo.cpp
// File: OpenMPDemo.cpp
// Coding: utf-8
#include <iostream>
#include <vector>
#include <cstdlib>
#include <omp.h>
#include <chrono>

void matrix_multiplication(const std::vector<std::vector<double>> &A,
													 const std::vector<std::vector<double>> &B,
													 std::vector<std::vector<double>> &C)
{
	int N = A.size();
	int M = B[0].size();
	int K = A[0].size();

#pragma omp parallel for
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < M; ++j)
		{
			for (int k = 0; k < K; ++k)
			{
				C[i][j] += A[i][k] * B[k][j];
			}
		}
	}
}

void matrix_multiplicationNoMP(const std::vector<std::vector<double>> &A,
															 const std::vector<std::vector<double>> &B,
															 std::vector<std::vector<double>> &C)
{
	int N = A.size();
	int M = B[0].size();
	int K = A[0].size();

	// #pragma omp parallel for
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < M; ++j)
		{
			for (int k = 0; k < K; ++k)
			{
				C[i][j] += A[i][k] * B[k][j];
			}
		}
	}
}

void TestMatrixMultiplication(const std::vector<std::vector<double>> &A,
															const std::vector<std::vector<double>> &B,
															std::vector<std::vector<double>> &C, bool bOpenMP)
{
	// double start_time = omp_get_wtime();
	auto start = std::chrono::high_resolution_clock::now();

	// 执行矩阵乘法
	if (bOpenMP)
		matrix_multiplication(A, B, C);
	else
		matrix_multiplicationNoMP(A, B, C);

	// double end_time = omp_get_wtime();
	auto stop = std::chrono::high_resolution_clock::now();
	auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);

	if (bOpenMP)
		std::cout << "Using OpenMP:" << std::endl;
	else
		std::cout << "Bot using OpenMP:" << std::endl;
	std::cout << "Time taken: " << duration.count() / 1000.0 << " seconds." << std::endl;
	// std::cout << "Time taken: " << end_time - start_time << " seconds." << std::endl;
}

int main()
{
	//Heroius: 将维度从2000减小到500,以加快调试
	const int N = 500; // 设置矩阵的维度
	std::vector<std::vector<double>> A(N, std::vector<double>(N));
	std::vector<std::vector<double>> B(N, std::vector<double>(N));
	std::vector<std::vector<double>> C(N, std::vector<double>(N, 0));

// 使用随机数填充矩阵 A 和 B
#pragma omp parallel for
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < N; ++j)
		{
			A[i][j] = rand() / 100.0;
			B[i][j] = rand() / 100.0;
		}
	}

	TestMatrixMultiplication(A, B, C, true);
	TestMatrixMultiplication(A, B, C, false);

	std::cin.get();
	return 0;
}

MakeList

CMakeLists.txt
 # File: CMakeLists.txt
# Heroius: min version of cmake who supports IntelLLVM is 3.25 according to https://www.intel.com/content/www/us/en/docs/dpcpp-cpp-compiler/developer-guide-reference/2023-2/use-cmake-with-the-compiler.html
cmake_minimum_required(VERSION 3.25)

# 指定使用C++11标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# Set the project name
# Heroius: add CXX parameter to use cpp compiler only, so the c language settings are no longer needed
project(OpenMPDemo CXX)

# Look for Intel oneAPI CMake toolchain files
# find_package shall be called after project command 
find_package(IntelSYCL REQUIRED)

# 指定 C++ 编译器
# Heroius: compilers could only be set in user toolchain file according to https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html
# Heroius: however the effective settings depands on the cmake generator, see Configure_CMake.bat command line
#set(CMAKE_CXX_COMPILER "C:/Program Files (x86)/Intel/oneAPI/compiler/latest/windows/bin/icpx.exe")
# 指明编译工具链
# Heroius: it is suggested that generator toolset set only in a toolchain file according to https://cmake.org/cmake/help/latest/variable/CMAKE_GENERATOR_TOOLSET.html
# Heroius: however the effective settings depands on the cmake generator, see Configure_CMake.bat command line
#set(CMAKE_GENERATOR_TOOLSET "Intel(R) oneAPI DPC++ Compiler 2023")

# Set executable path
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)

# OpenMP 编译标志
# Heroius: OpenMP flags should be '/Qiopenmp' according to https://www.intel.com/content/www/us/en/docs/oneapi/programming-guide/2023-0/c-c-or-fortran-with-openmp-offload-programming.html
# Heroius: however the find_package() commands will set those flags properly itself
#set(OpenMP_CXX_FLAGS "${OpenMP_CXX_FLAGS} /Qiopenmp")  
# OpenMP 库名称
# Heroius: Their should not be space between symbol 'OpenMP_C_LIB_NAMES' and 'libiomp5'
#set(OpenMP_CXX_LIB_NAMES "${OpenMP_CXX_LIB_NAMES}libiomp5") 
#set(OpenMP_libiomp5_LIBRARY "${OpenMP_CXX_LIB_NAMES}")

# 查找OpenMP库
# Heroius: under this instruction, cmake will call 'c:\Program Files\CMake\share\cmake-3.27\Modules\FindOpenMP.cmake' file, in the lines 605-606 of which, the 'if' command has grammatical error:
# Heroius: the 2nd 'CMAKE_${LANG}_COMPILER_ID' should not enbraced in command: if(CMAKE_${LANG}_COMPILER_ID STREQUAL "Fujitsu" OR ${CMAKE_${LANG}_COMPILER_ID} STREQUAL "IntelLLVM")
# Heroius: notice: back up the file before editing FindOpenMP.cmake with admin privilege!
find_package(OpenMP REQUIRED)

# Add the executable
add_executable(OpenMPDemo OpenMPDemo.cpp)

add_sycl_to_target(TARGET OpenMPDemo SOURCES OpenMPDemo.cpp)

# 如果找到OpenMP库,链接到目标可执行文件
if(OpenMP_CXX_FOUND)
    target_link_libraries(OpenMPDemo OpenMP::OpenMP_CXX)
endif()

Configure脚本

Configure_CMake.bat
 :: 如果存在 Build,删除
if exist Build rd /S /Q Build 
:: 如果不存在 Build,创建
if not exist Build md Build
:: 调用批处理设置 Intel oneAPI 的编译环境
Call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" intel64 vs2022
Set Lib=%lib%;"C:\Program Files (x86)\Intel\oneAPI\compiler\2023.2.0\windows\compiler\lib\intel64_win"
:: 当前文件夹为 Source,子文件夹 .\Build 为构建目录
:: Heroius: the default cmake generator is 'Visual Studio 17 2022', which is not fully featured.
:: Heroius: it seems the Visual Studio generator allways reset compilers to MSVC.
:: Heroius: the only Intel compiler supports cmake is icx, and the only generator supports icx is ninja according to https://www.intel.com/content/www/us/en/docs/dpcpp-cpp-compiler/developer-guide-reference/2023-2/use-cmake-with-the-compiler.html 
cmake -G Ninja -DCMAKE_CXX_COMPILER=icx  -S . -B .\Build 
pause

编译脚本

Build_CMake.bat
 :: 调用批处理设置 Intel oneAPI 的编译环境
:: Heroius: enable basic environments to make sure the std libs could be found
Call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" intel64 vs2022
:: Set MPI compiler environment
Call "C:\Program Files (x86)\Intel\oneAPI\mpi\Latest\env\vars.bat"
:: 进入构建目录
cd Build
:: 构建,项目文件在当前目录
cmake --build .
pause

运行脚本

Run_App.bat
 @ECHO OFF
:: 加载ONEAPI环境,使得引用库在运行环境可见;将加载过程的输出重定向隐藏
Call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" intel64 vs2022 >NUL 2>&1
cd ./build/bin/
OpenMPDemo.exe

注意事项

  • 支持2023.2版本IntelLLVM编译器的CMake最低版本为3.25
  • 在project()中,指定CXX语言,就不用配置C语言选项(CMake默认启用C和C++)
  • CMAKE_CXX_COMPILER 、CMAKE_GENERATOR_TOOLSET 等变量建议通过 toolchain 文件设置
  • CMake使用的默认生成器为 Visual Studio 17 2022,因尚未探明之原因,其总将编译器设置为MSVC,故若要使用Intel编译器,推荐使用Ninja生成器
  • 在Intel编译器中,支持CMake的是icx
  • 编译时,需要设置OneAPI环境以使引用的标准库可见
  • 使用find_package(OpenMP REQUIRED)即可启用OpenMP支持,无需另行设置编译标记