Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Griffin_Myers
Beginner
169 Views

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:

[cpp]#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;
    }
}
[/cpp]

  • 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)
0 Kudos
14 Replies
SKost
Valued Contributor II
169 Views

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

Griffin_Myers
Beginner
169 Views

Sergey,

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

-Griffin
hydroxyprolin
Beginner
169 Views

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.
SKost
Valued Contributor II
169 Views

>>...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
chris_cramer
Beginner
169 Views

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
chris_cramer
Beginner
169 Views

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

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

chris_cramer
Beginner
169 Views

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
SKost
Valued Contributor II
169 Views

>>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.
hydroxyprolin
Beginner
169 Views

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.
Michael_K_Intel2
Employee
169 Views

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
SKost
Valued Contributor II
169 Views

I look forward to see results of investigation. Thank you, Michael!
SKost
Valued Contributor II
169 Views

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.

Anonymous57
Beginner
169 Views

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