Clang, OpenMP и пользовательское сокращение вектора / матрицы - PullRequest
0 голосов
/ 12 сентября 2018

Мне до сих пор приходилось использовать доморощенный gcc для компиляции кода, дополненного OMP, на моем Mac.

Хорошая новость заключается в том, что Apple Clang теперь может находить заголовки OMP (по крайней мере, в Apple LLVM version 9.1.0 (clang-902.0.39.2) версии).

Плохая новость заключается в том, что пользовательские предложения сокращения, которые раньше работали, не работают.больше.Я приложил фрагмент кода ниже, который демонстрирует мою проблему.Он сразу падает при входе в параллельный блок с ошибкой сегмента или следующей ошибкой:

DebugOMP(46436,0x7fff8fc12380) malloc: *** error for object 0x7fff8fc02000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

Есть ли способ исправить это сокращение?Более простые предложения OMP, такие как #pragma omp parallel for, работают нормально.Я использую Armadillo 9.100.5.Та же проблема происходит с Eigen.

main.cpp:

#include <armadillo>

#pragma omp declare reduction( + : arma::vec : omp_out += omp_in ) \
initializer( omp_priv = omp_orig )


int main() {

    int N = 10000;
    int M = 100;
    double a = 0;

    // Built-in reduction, works
    #pragma omp parallel for reduction(+:a)
    for (int k = 0; k < M; ++k){
        a += k;
    }

    std::cout << a << std::endl;

    arma::vec v = arma::zeros<arma::vec>(M);

    // Parallel access, works
    #pragma omp parallel for
    for (int k = 0; k < M; ++k){
        v(k) = k;
    }

    std::cout << v << std::endl;

    // Custom, reduction, segfaults
    #pragma omp parallel for reduction(+:v)
    for (int i = 0; i < N; ++i){
        v += arma::ones<arma::vec>(v.n_rows);
    }

    std::cout << v << std::endl;

    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.0)

# Building procedure
get_filename_component(dirName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(EXE_NAME ${dirName} CACHE STRING "Name of executable to be created.")

project(${EXE_NAME})

# Find Armadillo 
find_package(Armadillo REQUIRED )
include_directories(${ARMADILLO_INCLUDE_DIRS})

# Find OpenMP
find_package(OpenMP)
if(OPENMP_FOUND)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()

# Add source files in root directory
add_executable(${EXE_NAME}
main.cpp)


# Linking
set(library_dependencies ${ARMADILLO_LIBRARIES} )


target_link_libraries(${EXE_NAME} ${library_dependencies} OpenMP::OpenMP_CXX)

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Так как первоначальный вопрос также задавался о Eigen, вот отдельный пример работы с gcc 5, 6, 7, 8 и clang 6. Он segfaults с clang 5, вероятно, ошибка на стороне clang 5.По сути, это то же решение, которое предложено Z-бозоном.

#include <Eigen/Core>
#include <iostream>
using namespace Eigen;

typedef VectorXd vec;

#pragma omp declare reduction( + : vec : omp_out += omp_in ) \
  initializer( omp_priv = vec::Zero(omp_orig.size()) )

int main() {
    int N = 10000;
    int M = 100;
    vec v = vec::LinSpaced(M,0,M-1);

    #pragma omp parallel for reduction(+:v)
    for (int i = 0; i < N; ++i){
        v += vec::Ones(v.size());
    }

    std::cout << v.transpose() << std::endl;
    return 0;
}
0 голосов
/ 12 сентября 2018

Вы можете сделать сокращение вручную следующим образом:

#pragma omp parallel
{
  arma::vec t = arma::zeros<arma::vec>(M);
  #pragma omp for nowait
  for (int i = 0; i < N; ++i) t += arma::ones<arma::vec>(v.n_rows);
  #pragma omp critical
  v += t;
}

Это работает с Clang.Это может помочь вам понять, как определить initializer-expr

Например, это работает с GCC 7

#pragma omp declare reduction( + : arma::vec : omp_out += omp_in ) \
initializer( omp_priv = arma::zeros<arma::vec>(omp_orig.n_rows))

Однако с Clang 5.0 код зависает, поэтому я не уверен, что Clang'sпроблема есть.Я пробовал другие варианты initializer-expr, но ни один из них не заставил Clang работать.


Я установил clang7, и код OP работает нормально.В общем, я думаю, что лучше было бы явно установить вектор на ноль, например,

initializer( omp_priv = arma::zeros<arma::vec>(omp_orig.n_rows))

, а не неявно, как это

initializer(omp_priv = omp_orig)

, поскольку неявный случай предполагает, что конструктор инициализируется какноль.

...