OpenMP с MSVC 2010 Отладка сборки странная ошибка при копировании объекта

У меня довольно сложная программа, которая работает странным образом при сборке с OpenMP в режиме отладки MSVC 2010. Я старался изо всех сил построить следующий минимальный рабочий пример (хотя он не является на самом деле минимальным), который минимизирует структуру реальной программы.

#include <vector>
#include <cassert>

// A class take points to the whole collection and a position Only allow access
// to the elements at that posiiton. It provide read-only access to query some
// information about the whole collection
class Element
{
    public :

    Element (int i, std::vector<double> *src) : i_(i), src_(src) {}

    int i () const {return i_;}
    int size () const {return src_->size();}

    double src () const {return (*src_)[i_];}
    double &src () {return (*src_)[i_];}

    private :

    const int i_;
    std::vector<double> *const src_;
};

// A Base class for dispatch
template <typename Derived>
class Base
{
    protected :

    void eval (int dim, Element elem, double *res)
    {
        // Dispatch the call from Evaluation<Derived>
        eval_dispatch(dim, elem, res, &Derived::eval); // Point (2)
    }

    private :

    // Resolve to Derived non-static member eval(...)
    template <typename D>
    void eval_dispatch(int dim, Element elem, double *res,
            void (D::*) (int, Element, double *))
    {
#ifndef NDEBUG // Assert that this is a Derived object
        assert((dynamic_cast<Derived *>(this)));
#endif
        static_cast<Derived *>(this)->eval(dim, elem, res);
    }

    // Resolve to Derived static member eval(...)
    void eval_dispatch(int dim, Element elem, double *res,
            void (*) (int, Element, double *))
    {
        Derived::eval(dim, elem, res); // Point (3)
    }

    // Resolve to Base member eval(...), Derived has no this member but derived
    // from Base
    void eval_dispatch(int dim, Element elem, double *res,
            void (Base::*) (int, Element, double *))
    {
        // Default behavior: do nothing
    }
};

// A middle-man who provides the interface operator(), call Base::eval, and
// Base dispatch it to possible default behavior or Derived::eval
template <typename Derived>
class Evaluator : public Base<Derived>
{
    public :

    void operator() (int N , int dim, double *res)
    {
        std::vector<double> src(N);
        for (int i = 0; i < N; ++i)
            src[i] = i;

#pragma omp parallel for default(none) shared(N, dim, src, res)
        for (int i = 0; i < N; ++i) {
            assert(i < N);
            double *r = res + i * dim;
            Element elem(i, &src);
            assert(elem.i() == i); // Point (1)
            this->eval(dim, elem, r);
        }
    }
};

// Client code, who implements eval
class Implementation : public Evaluator<Implementation>
{
    public :

    static void eval (int dim, Element elem, double *r)
    {
        assert(elem.i() < elem.size()); // This is where the program fails Point (4)
        for (int d = 0; d != dim; ++d)
            r[d] = elem.src();
    }
};

int main ()
{
    const int N = 500000;
    const int Dim = 2;
    double *res = new double[N * Dim];
    Implementation impl;
    impl(N, Dim, res);
    delete [] res;

    return 0;
}

Реальная программа не имеетvector и т. д. НоElement, Base, Evaluator а такжеImplementation захватывает основную структуру реальной программы. При сборке в режиме отладки и запуске отладчика утверждение не выполняется приPoint (4).

Вот еще некоторые подробности отладочной информации, просматривая стеки вызовов,

При входеPoint (1), местныйi имеет значение371152это нормально. Переменнаяelem не отображается в кадре, что немного странно. Но так как утверждение вPoint (1) не подвела, я думаю это нормально.

Затем произошли безумные вещи. Призыв кeval отEvaluator разрешается в свой базовый класс, и такPoint (2) был вычеркнут. На этом этапе отладчики показывают, чтоelem имеетi_ = 499999который больше не являетсяi используется для созданияelem вEvaluator прежде чем передать егоby value вBase::eval, Следующий момент, он решаетPoint (3), этот раз,elem имеетi_ = 501682, который находится вне диапазона, и это значение, когда вызов направлен наPoint (4) и провалил утверждение.

Похоже, всякий раз, когдаElement Объект передается по значению, значения его членов изменяются. Повторно запустите программу несколько раз, подобное поведение происходит, хотя и не всегда воспроизводимо. В реальной программе этот класс разработан как итератор, который выполняет итерацию по совокупности частиц. Хотя вещь, которую он повторяет, не является чем-то вроде контейнера. Но в любом случае, дело в том, что он достаточно мал, чтобы эффективно передаваться по значению. И, следовательно, клиентский код знает, что у него есть собственная копияElement вместо некоторой ссылки или указателя, и не нужно беспокоиться о поточно-ориентированном (много), пока он придерживаетсяElementинтерфейс, который обеспечивает доступ на запись только к одной позиции всей коллекции.

Я попробовал ту же программу с GCC и Intel ICPC. Ничего неожиданного не происходит. А в реальной программе правильные результаты там, где получены.

Я где-то использовал OpenMP неправильно? Я думал, чтоelem создано околоPoint (1) должен быть локальным для тела цикла. Кроме того, во всей программе нет значения больше, чемN был произведен, так откуда же взялись эти новые ценности?

Edit

Я более внимательно посмотрел на отладчик, он показывает, что в то время какelem.i_ был изменен, когдаelem был передан по значению, указательelem.src_ не меняется с этим. Имеет то же значение (адреса памяти) после передачи по значению

Edit: Compiler flags

Я использовал CMake для генерации решения MSVC. Я должен признаться, я понятия не имею, как использовать MSVC или Windows в целом. Единственная причина, по которой я его использую, заключается в том, что я знаю, что многие люди используют его, поэтому я хочу протестировать свою библиотеку на предмет обхода любых проблем.

CMake сгенерировал проект, используяVisual Studio 10 Win64 цель, флаги компилятора /DWIN32 /D_WINDOWS /W3 /Zm1000 /EHsc /GR /D_DEBUG /MDd /Zi /Ob0 /Od /RTC1 А вот командная строка, найденная в Property Pages-C / C ++ - Командная строка /Zi /nologo /W3 /WX- /Od /Ob0 /D "WIN32" /D "_WINDOWS" /D "_DEBUG" /D "CMAKE_INTDIR=\"Debug\"" /D "_MBCS" /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /GR /openmp /Fp"TestOMP.dir\Debug\TestOMP.pch" /Fa"Debug" /Fo"TestOMP.dir\Debug\" /Fd"C:/Users/Yan Zhou/Dropbox/Build/TestOMP/build/Debug/TestOMP.pdb" /Gd /TP /errorReport:queue

Здесь есть что-нибудь подозрительное?

Ответы на вопрос(1)

Ваш ответ на вопрос