OpenMP firstprivate and C++ object destruction

OpenMP firstprivate and C++ object destruction

I'm experiencing some unexpected behavior with icpc when using C++ objects with a firstprivate vs. private clause. When an object is private then it appears to be be both constructed and destructed as expected, but for firstprivate the object does not seem to be destructed. g++ does not exhibit similar behavior. My understanding is that firstprivate C++ objects should be destructed (albeit in an unpredictable order) -- so is this an icpc bug or am I misunderstanding the OpenMP spec?

Consider the following contrived code:

#include 
#include 
#include 

using namespace std;

class foo
{
    public:
        foo(int v=0) : _bar(v){}
        ~foo() {cout << "destructing: " << _bar << endl;}
        int Bar() {return _bar;}

    private:
        int _bar;
};


int main()
{
    foo g(5);
    foo f(99);

#pragma omp parallel for default(none) shared(std::cout) private(f) num_threads(3)
    for (int i=0; i<3; i++)
    {
        usleep(omp_get_thread_num()*1e6);
        cout << omp_get_thread_num() << ": " << f.Bar() << endl;
    }
}

  • With this code, g++ and icpc produce identical results. Here each instance of f inside the loop is instantiated with the default constructor (as each of the _bar values is seen to be the default value of 0)

g++ produces:
0: 0
destructing: 0
1: 0
destructing: 0
2: 0
destructing: 0
destructing: 99
destructing: 5

icpc produces:
0: 0
destructing: 0
1: 0
destructing: 0
2: 0
destructing: 0
destructing: 99
destructing: 5

  • Now change the private clause on line 24 to firstprivate. We see that instantiation now occurs with the implicit copy constructor (as _bar is 99). But, for the icpc case none of the private copies of f get destructed!

g++ produces:
0: 99
destructing: 99
1: 99
destructing: 99
2: 99
destructing: 99
destructing: 99
destructing: 5

icpc produces:
0: 99
1: 99
2: 99
destructing: 99
destructing: 5

Version info:
icpc (ICC) 12.1.0 20111011
g++44 (GCC) 4.4.4 20100726 (Red Hat 4.4.4-13)

15 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Griffin,

Did you try to declare the destructor as 'virtual'?

classfoo

{
public:
...
virtual ~foo(){ cout<<"destructing:"<<_bar< ...
};

I know that it sounds as astrange advice because there areNo Runtime Binding in your Test-Case. Anyway, I would try.

Best regards,
Sergey

Sergey,

I just tried re-running with a virtual destructor, but it did not change the results.

-Griffin

In my opinion this is this is a bug in icpc (verified with latest Version 12.1.2.273 Build 20111128 / Linux x86-64), skipping the destructors doesn't make any sense at all.

As a workaround you may add 'lastprivate(f)' to 'firstprivate(f)', then all destructors are called as they should. Because without the lastprivate clause the value of f has an indeterminate value upon exit from the construct, this workaround does not affect any code after the work-sharing construct.

>>...this is a bug in icpc...

I would also check the code with commented out '#pragma openmp ...' directive and lines 25 to 29 in
order to see that the C/C++ compiler does everything well and destructors are called.

Another verification before claiming that "this is a bug" is a Test-Case for a Runtime Binding of C++
objects. I had some issues with g++ version 3.4.2 in the past.

Best regards,
Sergey

I'll test without the #pragma, but I've never had any issues of not having destructors called and the code does call the destructor when the variable is private.

FWIW, I've tested this under Windows running Visual Studio 2008 with the Intel Compiler plugin turned on and the results are the same.

-chris

and to confirm, compiling without the openmp #pragma results in the creation, construction and destruction of both objects for both g++ and icpc.-chris

Here is a Test-Case for virtual destructors when a Runtime Binding is used:

class A
{
public:
A( void ){ printf( "A::A\n" ); };
virtual ~A( void ){ printf( "A::~A\n" ); };
};

class V
{
public:
V( void ){ printf( "V::V\n" ); };
virtual ~V( void ){ printf( "V::~V\n" ); };
//~V( void ){ printf( "V::~V\n" ); };// If it is declared as Non Virtual ~W() won't be called and
}; // "resources" won't be released

class W : public V
{
public:
W( void ){ printf( "W::W\n" ); };
virtual ~W( void ){ printf( "W::~W\n" ); };
//~W( void ){ printf( "W::~W\n" ); };
};

void main( void )
{
A *pA = new A();

printf( "\n" );

V *pv = new V();
delete pv; // pv->~V::V(); // Used ::operator delete()
printf( "\n" );

pv = new W();
delete pv; // pv->~W::W(); // Used W::operator delete()
printf( "\n" );

pv = new W();
::delete pv;// pv->~W::W(); // Used ::operator delete()
printf( "\n" );

delete pA;
}

Sure - and that makes perfect sense in the case you are showing. But what does that have to do with the original question? The openmp issue with not calling the destructor when firstprivate is used occurs in the simple test case that was presented which does not make use of run-time bindings, does make use of openmp, and behaves differently when compiled with g++ vs icpc. None of these things are true for the textbook demonstration of virtual destructors above. -chris

>>But what does that have to do with the original question?..

This is to verify that a C/C++ compiler produces everything right without OpenMP. Especially, such a
fundamental thing as calling a virtual destructor.

>>...None of these things are true for the textbook demonstration of virtual destructors...

This is not a text-book example. I have several tests for virtual destructors in my set of Test-Cases. I
usethese Test-Casesbecause in our Build Environment four different C/C++ compilers are used and
two more are coming. It allows to verify that regardless of a C/C++ compiler, or OpenMP in your case,
there are no bugs and Test-Cases produce absolutely identical outputs.

I don't see any relevance of the test case from Sergey for this problem either. The behaviour of icpc is a violation of the OpenMP specification, so it is a bug.

To intel staff: please create a ticket for fixing this bug ("objects created for OpenMP work-sharing constructs with 'firstprivate' clause are not destructed"), a test case is supplied with the first post by Griffin Myers.

Folks,

I will have a look into this. From your description, it seems that the compiler indeed does not fully respect the OpenMP spec. I will check with our compiler team about a solution.

Cheers,
-michael

I look forward to see results of investigation. Thank you, Michael!

I just tested the original Test-Case with VS 2005 on Windows XPandI hope thatmy reportwill be useful:

1. #pragma omp parallel for default( none ) shared( std::cout ) private( f ) num_threads( 3 )

>> g++ produces << >> VS 2005 produces <<
0: 0 0: 0
destructing: 0 destructing: 0
1: 0 1: 0
destructing: 0 destructing: 0
2: 0 2: 0
destructing: 0 destructing: 0
destructing: 99 destructing: 99
destructing: 5 destructing: 5

2. #pragma omp parallel for default( none ) shared( std::cout ) firstprivate( f ) num_threads( 3 )

>> g++ produces << >> VS 2005 produces<<
0: 99 0: 99
destructing: 99 destructing: 99
1: 99 1: 99
destructing: 99 destructing: 99
2: 99 2: 99
destructing: 99 destructing: 99
destructing: 99 destructing: 99
destructing: 5 destructing: 5

PS1: My modifications to the Test-Case are as follows:

1.
//#include

2.
Instead of

usleep( omp_get_thread_num() * 1e6 );

I used

::Sleep( omp_get_thread_num() * 1e3 );

PS2: Memory Leaks are NOT detected.

A defect has beenfiled against both our 32-bit and 64-bit version of the compiler. Thank you for highlighting this issue.

Leave a Comment

Please sign in to add a comment. Not a member? Join today