Note that there are some explanatory texts on larger screens.

plurals
  1. POOpenMP with MSVC 2010 Debug build strange bug when object are copied
    primarykey
    data
    text
    <p>I have a fairly complex program that runs into strange behavior when build with OpenMP in MSVC 2010 Debug mode. I have tried my best to construct the following minimal working example (though it is not really minimal) which minic the structure of the real program.</p> <pre><code>#include &lt;vector&gt; #include &lt;cassert&gt; // 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&lt;double&gt; *src) : i_(i), src_(src) {} int i () const {return i_;} int size () const {return src_-&gt;size();} double src () const {return (*src_)[i_];} double &amp;src () {return (*src_)[i_];} private : const int i_; std::vector&lt;double&gt; *const src_; }; // A Base class for dispatch template &lt;typename Derived&gt; class Base { protected : void eval (int dim, Element elem, double *res) { // Dispatch the call from Evaluation&lt;Derived&gt; eval_dispatch(dim, elem, res, &amp;Derived::eval); // Point (2) } private : // Resolve to Derived non-static member eval(...) template &lt;typename D&gt; 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&lt;Derived *&gt;(this))); #endif static_cast&lt;Derived *&gt;(this)-&gt;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 &lt;typename Derived&gt; class Evaluator : public Base&lt;Derived&gt; { public : void operator() (int N , int dim, double *res) { std::vector&lt;double&gt; src(N); for (int i = 0; i &lt; N; ++i) src[i] = i; #pragma omp parallel for default(none) shared(N, dim, src, res) for (int i = 0; i &lt; N; ++i) { assert(i &lt; N); double *r = res + i * dim; Element elem(i, &amp;src); assert(elem.i() == i); // Point (1) this-&gt;eval(dim, elem, r); } } }; // Client code, who implements eval class Implementation : public Evaluator&lt;Implementation&gt; { public : static void eval (int dim, Element elem, double *r) { assert(elem.i() &lt; 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; } </code></pre> <p>The real program does not have <code>vector</code> etc. But the <code>Element</code>, <code>Base</code>, <code>Evaluator</code> and <code>Implementation</code> captures the basic structure of the real program. When build in Debug mode, and running the debugger, the assertion fails at <code>Point (4)</code>.</p> <p>Here is some more details of the debug informations, by viewing the call stacks,</p> <p>At entering <code>Point (1)</code>, the local <code>i</code> has value <code>371152</code>, which is fine. The variable <code>elem</code> does not shown up in the frame, which is a little strange. But since the assertion at <code>Point (1)</code> does not faile, I guess it is fine.</p> <p>Then, crazy things happened. The call to <code>eval</code> by <code>Evaluator</code> resolves to its base class, and so <code>Point (2)</code> was exectuted. At this point, the debugers shows that the <code>elem</code> has <code>i_ = 499999</code>, which is no longer the <code>i</code> used to create <code>elem</code> in <code>Evaluator</code> before passing it <strong>by value</strong> to <code>Base::eval</code>. The next point, it resolves to <code>Point (3)</code>, this time, <code>elem</code> has <code>i_ = 501682</code>, which is out of range, and this is the value when the call is directed to <code>Point (4)</code> and failed the assertion.</p> <p>It looks like whenever <code>Element</code> object is passed by value, the value of its members are changed. Rerun the program multiple times, similar behaviors happens though not always reproducible. In the real program, this class is designed to like an iterator, which iterate over a collection of particles. Though the thing it iterate is not exaclty like a container. But anyway, the point is that it is small enough to be efficiently passed by value. And therefore, the client code, knows that it has its own copy of <code>Element</code> instead of some reference or pointer, and does not need to worry about thread-safe (much) as long as he sticks with <code>Element</code>'s interface, which only provide write access to a single position of the whole collection.</p> <p>I tried the same program with GCC and Intel ICPC. Nothing un-expected happens. And in the real program, correct results where produced.</p> <p>Did I used OpenMP wrongly somewhere? I thought that the <code>elem</code> created at about <code>Point (1)</code> shall be local to the loop body. In addition, in the whole program, no value bigger than <code>N</code> was produced, so where does the those new value comes from?</p> <p><strong>Edit</strong></p> <p>I looked more carefully into the debugger, it shows that while <code>elem.i_</code> was changed when <code>elem</code> was passed by value, the pointer <code>elem.src_</code> does not change with it. It has the same value (of the memory address) after passed by value</p> <p><strong>Edit: Compiler flags</strong></p> <p>I used CMake to generate the MSVC solution. I have to confess I have no idea how to use MSVC or Windows in general. The only reason I am using it is that I know a lot of people use it so I want to test my library against it to workaround any problems.</p> <p>The CMake generated project, using <code>Visual Studio 10 Win64</code> target, the compiler flags appears to be <code>/DWIN32 /D_WINDOWS /W3 /Zm1000 /EHsc /GR /D_DEBUG /MDd /Zi /Ob0 /Od /RTC1</code> And here is the command line found in Property Pages-C/C++-Command Line <code>/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</code></p> <p>Is there anything suspecious here?</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload