Visual Studio では OpenMP のビルドフラグを付けなくても OpenMP の API を使用できる

試した環境

本題

Visual Studio では OpenMP で並列化をする際にはビルドフラグ/openmpが必要ですが、omp_get_num_thread()などの OpenMPAPI はこのビルドフラグがなくてもビルドエラーなどにはならず、値も適当な値が返ってきます。

確認のために試したソースコードは以下のとおりです。

#include <iostream>
#include <omp.h>

int main(int argc, char *argv[]) {
    std::cout << "omp_get_max_thread() = " << omp_get_max_threads() << std::endl;
    std::cout << "omp_get_num_thread() = " << omp_get_num_threads() << std::endl;
    std::cout << "omp_get_thread_num() = " << omp_get_thread_num() << std::endl;

#pragma omp parallel for
    for (int i = 0; i < 32; ++i) {
#pragma omp critical
        {
            std::cout << "i = " << i << " : omp_get_num_thread() = " << omp_get_num_threads() << " omp_get_thread_num() = " << omp_get_thread_num() << std::endl;
        }
    }
    return 0;
}

上記ソースコードを以下の内容のCmakeLists.txtでビルドします。 /openmpフラグを付けないときには最後の4行をコメントアウトします。

cmake_minimum_required(VERSION 3.5.1)
project(omp_project)

if(MSVC)
    string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /EHsc")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DBUILD_TYPE=\\\"Debug\\\"")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /DBUILD_TYPE=\\\"Release\\\"")
else()
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DBUILD_TYPE=\\\"Debug\\\"")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DBUILD_TYPE=\\\"Release\\\"")
endif()

add_executable(omp_project main.cpp)

find_package(OpenMP REQUIRED)
if(OpenMP_FOUND)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()

実行結果は以下のとおりです。 /openmpフラグを付けなかったときでもomp_get_max_thread()は実行環境の論理プロセッサ数の8を返し、 omp_get_num_thread()が1スレッドで動いていることを表しています。

/openmpフラグを付けてビルドしたとき。

$ omp_project.exe
omp_get_max_thread() = 8
omp_get_num_thread() = 1
omp_get_thread_num() = 0
i = 20 : omp_get_num_thread() = 8 omp_get_thread_num() = 5
i = 21 : omp_get_num_thread() = 8 omp_get_thread_num() = 5
i = 22 : omp_get_num_thread() = 8 omp_get_thread_num() = 5
i = 23 : omp_get_num_thread() = 8 omp_get_thread_num() = 5
i = 16 : omp_get_num_thread() = 8 omp_get_thread_num() = 4
i = 17 : omp_get_num_thread() = 8 omp_get_thread_num() = 4
i = 18 : omp_get_num_thread() = 8 omp_get_thread_num() = 4
i = 19 : omp_get_num_thread() = 8 omp_get_thread_num() = 4
i = 0 : omp_get_num_thread() = 8 omp_get_thread_num() = 0
i = 1 : omp_get_num_thread() = 8 omp_get_thread_num() = 0
i = 2 : omp_get_num_thread() = 8 omp_get_thread_num() = 0
i = 3 : omp_get_num_thread() = 8 omp_get_thread_num() = 0
i = 28 : omp_get_num_thread() = 8 omp_get_thread_num() = 7
i = 29 : omp_get_num_thread() = 8 omp_get_thread_num() = 7
i = 30 : omp_get_num_thread() = 8 omp_get_thread_num() = 7
i = 31 : omp_get_num_thread() = 8 omp_get_thread_num() = 7
i = 12 : omp_get_num_thread() = 8 omp_get_thread_num() = 3
i = 13 : omp_get_num_thread() = 8 omp_get_thread_num() = 3
i = 14 : omp_get_num_thread() = 8 omp_get_thread_num() = 3
i = 15 : omp_get_num_thread() = 8 omp_get_thread_num() = 3
i = 4 : omp_get_num_thread() = 8 omp_get_thread_num() = 1
i = 5 : omp_get_num_thread() = 8 omp_get_thread_num() = 1
i = 6 : omp_get_num_thread() = 8 omp_get_thread_num() = 1
i = 7 : omp_get_num_thread() = 8 omp_get_thread_num() = 1
i = 24 : omp_get_num_thread() = 8 omp_get_thread_num() = 6
i = 25 : omp_get_num_thread() = 8 omp_get_thread_num() = 6
i = 26 : omp_get_num_thread() = 8 omp_get_thread_num() = 6
i = 27 : omp_get_num_thread() = 8 omp_get_thread_num() = 6
i = 8 : omp_get_num_thread() = 8 omp_get_thread_num() = 2
i = 9 : omp_get_num_thread() = 8 omp_get_thread_num() = 2
i = 10 : omp_get_num_thread() = 8 omp_get_thread_num() = 2
i = 11 : omp_get_num_thread() = 8 omp_get_thread_num() = 2

/openmpフラグを付けずにビルドしたとき。

$ omp_project.exe
omp_get_max_thread() = 8
omp_get_num_thread() = 1
omp_get_thread_num() = 0
i = 0 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 1 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 2 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 3 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 4 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 5 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 6 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 7 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 8 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 9 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 10 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 11 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 12 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 13 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 14 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 15 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 16 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 17 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 18 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 19 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 20 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 21 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 22 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 23 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 24 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 25 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 26 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 27 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 28 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 29 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 30 : omp_get_num_thread() = 1 omp_get_thread_num() = 0
i = 31 : omp_get_num_thread() = 1 omp_get_thread_num() = 0

おまけ

ちなみに g++ では上記のコードは-fopenmpフラグをつけなければビルドが通りませんでした。

$ g++ -fopenmp ../main.cpp -o omp_test.exe

$ g++ ../main.cpp -o omp_test.exe
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\msys64\tmp\ccLqi92o.o:main.cpp:(.text+0x31): undefined reference to `omp_get_max_threads'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\msys64\tmp\ccLqi92o.o:main.cpp:(.text+0x65): undefined reference to `omp_get_num_threads'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\msys64\tmp\ccLqi92o.o:main.cpp:(.text+0x99): undefined reference to `omp_get_thread_num'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\msys64\tmp\ccLqi92o.o:main.cpp:(.text+0xf6): undefined reference to `omp_get_num_threads'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\msys64\tmp\ccLqi92o.o:main.cpp:(.text+0x117): undefined reference to `omp_get_thread_num'
collect2.exe: error: ld returned 1 exit status