OpenMP z MSVC 2010 Debuguj dziwny błąd podczas kopiowania obiektu

Mam dość złożony program, który działa w dziwny sposób podczas budowania z OpenMP w trybie debugowania MSVC 2010. Starałem się jak najlepiej skonstruować następujący minimalny przykład pracy (choć nie jest to naprawdę minimalny), który minimalizuje strukturę prawdziwego programu.

#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;
}

Prawdziwy program nie mavector itd. Ale toElement, Base, Evaluator iImplementation rejestruje podstawową strukturę prawdziwego programu. Po zbudowaniu w trybie debugowania i uruchomieniu debugera twierdzenie kończy się niepowodzeniemPoint (4).

Oto kilka szczegółowych informacji o debugowaniu, przeglądając stosy połączeń,

Przy wjeździePoint (1), lokalnyi ma wartość371152, co jest w porządku. Zmiennaelem nie pojawia się w ramce, co jest trochę dziwne. Ale od czasu twierdzenia wPoint (1) nie zawodzi, chyba jest w porządku.

Potem wydarzyły się szalone rzeczy. Wezwanie doeval przezEvaluator rozwiązuje do swojej klasy bazowej i tak dalejPoint (2) został wyegzekwowany. W tym momencie debugery pokazują, żeelem mai_ = 499999, co nie jest jużi używane do tworzeniaelem wEvaluator przed podaniemwedług wartości doBase::eval. Następny punkt, to postanawiaPoint (3), tym razem,elem mai_ = 501682, który jest poza zakresem, i jest to wartość, do której kierowane jest połączeniePoint (4) i nie spełnił tego twierdzenia.

Wygląda jak zawszeElement obiekt jest przekazywany przez wartość, zmienia się wartość jego członków. Uruchom program wielokrotnie, podobne zachowania zdarzają się, choć nie zawsze powtarzalne. W prawdziwym programie ta klasa jest przeznaczona do iteratora, który iteruje po zbiorze cząstek. Chociaż to, co iteruje, nie jest tak bardzo jak pojemnik. Ale w każdym razie chodzi o to, że jest wystarczająco mały, aby można go było skutecznie przekazać według wartości. A zatem kod klienta wie, że ma własną kopięElement zamiast jakiegoś odwołania lub wskaźnika, i nie musi się martwić o bezpieczne wątki (dużo), dopóki się trzymaElementinterfejs, który zapewnia dostęp do zapisu tylko do jednej pozycji całej kolekcji.

Próbowałem tego samego programu z GCC i Intel ICPC. Nie dzieje się nic nieoczekiwanego. A w prawdziwym programie poprawne wyniki tam, gdzie powstały.

Czy gdzieś użyłem OpenMP nieprawidłowo? Myślałem, żeelem stworzony okołoPoint (1) powinien być lokalny względem ciała pętli. Ponadto w całym programie nie ma wartości większej niżN został wyprodukowany, więc skąd pochodzą te nowe wartości?

Edytować

Przyjrzałem się uważniej debugerowi, który to pokazujeelem.i_ został zmieniony kiedyelem został przekazany przez wartość, wskaźnikelem.src_ nie zmienia się wraz z nim. Ma tę samą wartość (adresu pamięci) po przekazaniu przez wartość

Edytuj: Flagi kompilatora

Użyłem CMake do wygenerowania rozwiązania MSVC. Muszę przyznać, że nie mam pojęcia, jak używać MSVC lub ogólnie systemu Windows. Jedynym powodem, dla którego go używam, jest to, że wiem, że wiele osób go używa, więc chcę przetestować moją bibliotekę, aby obejść wszelkie problemy.

Projekt wygenerowany przez CMake przy użyciuVisual Studio 10 Win64 cel, flagi kompilatora wydają się być/DWIN32 /D_WINDOWS /W3 /Zm1000 /EHsc /GR /D_DEBUG /MDd /Zi /Ob0 /Od /RTC1 A oto linia poleceń znajdująca się na stronach właściwości - C / C ++ - Wiersz poleceń/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

Czy jest tu coś podejrzanego?

questionAnswers(1)

yourAnswerToTheQuestion