NVC++ using external libraries

I have written a small program using std::for_each(std::execution::par_unseq in main and it is working fine.
Then I have moved this part out of main into a class called by main and it is still working fine.
Last step I have moved this class into a library, also compiled with -stdpar. Both, library and application could be compiled and linked, but at runtime it chrashes with “segmentation fault”.

Are there any restrictions using libraries dealing with parallel macros?

Background: We have lots of libraries doing complex calculations, all written in pure C++, compatible to standard 17, which we now want to be acceleratde using NVC++ compiler and GPUs.

Hi ubehr,

Are you creating static or dynamic libraries? Static should work fine, but we have a known issue with creating dynamic libraries that we’re investigating. We’ve added your issue to problem report TPR #29694 to track.

Thanks,
Mat

Hi Mat,

Thanks a lot for the quick reply. Yes, it was a dynamic library. So, for the moment I will use a static one.

Best regards

ubehr

image001.jpg

Hi Mat,

This solution was working fine and I was able to run lots of performance tests with amazing results.

Now I have tried the same with a real world example from my company. Some small libraries.

Its central point was a simple loop.

Doing it in serial, everything is working well.

std::for_each(stationen.begin(), stationen.end(),

(ApplicationsPaar& ap) { CalculationBase(ap); });

Doing it in parallel, either “par” or “par_unseq”, the source code was compiled and linked into static libraries.

std::for_each(std::execution::par, stationen.begin(), stationen.end(),

(ApplicationsPaar& ap) { CalculationBase(ap); });

But calling them from a simple application leads to an error.

nvlink error : Undefined reference to ‘_ZN7StationDIEv’ in ‘hostpwd/ …

Looks like undefined symbols.

So I have checked the static library with “nm”.

Yes, these symbols are really undefined.

What’s going wrong here?

Both with serial loop and with parallel loop libraries are compiling and linking without any message.

But in the case of parallel loop this was not correct. There are undefined symbols.

Is it a problem with the method called? Following your documentation, a static method with a reference to a simple class as argument is allowed for parallel processes on GPU.

And in my former examples this idea was working well.

But not here, so I am confused.

The header-file:

class MY_LIB_PUBLIC CalculatorBase

{

public:

CalculatorBase();

CalculatorBase(const CalculatorBase& toCopy);

virtual ~CalculatorBase();

CalculatorBase& operator =(const CalculatorBase& av);

protected:

void copyAttributes(const CalculatorBase& av);

public:

void CalculationBaseSeriell();

void CalculationBaseParallelCPU();

void CalculationBaseParallelGPU();

void CalculationIM();

public:

static void CalculationBase(ApplicationsPaar& AP);

public:

std::vector stationen;

};

And the relevant cpp-part. Simple distance predictions hidden behind an *-operator, marked in yellow.

void CalculatorBase::CalculationBase(ApplicationsPaar& stationen)

{

int i, ni;

int s, n_s = stationen.S.STATION.size();

int g, n_g = stationen.G.STATION.size();

for (s = 0; s < n_s; s++)

{

Station S = stationen.S.STATION[s];

GeometrieBasis* GBs = NULL;

if (S.sitePoint != NULL)

{

GBs = new GeometrieDatenKreis();

((GeometrieDatenKreis)GBs).S.Laenge_grad = S.sitePoint->SID_int_DECIMAL;

((GeometrieDatenKreis)GBs).S.Breite_grad = S.sitePoint->SID_LAT_DECIMAL;

((GeometrieDatenKreis)GBs).r_m = 0.0;

}

else if (S.siteCircle != NULL)

{

GBs = new GeometrieDatenKreis();

((GeometrieDatenKreis)GBs).S.Laenge_grad = S.siteCircle->SID_int_DECIMAL;

((GeometrieDatenKreis)GBs).S.Breite_grad = S.siteCircle->SID_LAT_DECIMAL;

((GeometrieDatenKreis)GBs).r_m = S.siteCircle->ST_AREA_RADIUS;

}

else if (S.siteVector != NULL)

{

switch (S.siteVector->SID_TYPE)

{

case SiteVector::Type::closed:

ni = S.siteVector->SID_VECTOR.size();

GBs = new GeometrieDatenKontur();

for (i = 0; i < ni; i++)

{

GeoKoordinate GK;

GK.Laenge_grad = S.siteVector->SID_VECTOR[i].SID_int_DECIMAL;

GK.Breite_grad = S.siteVector->SID_VECTOR[i].SID_LAT_DECIMAL;

((GeometrieDatenKontur)GBs).PL.PL.push_back(GK);

}

break;

case SiteVector::Type::list:

ni = S.siteVector->SID_VECTOR.size();

GBs = new GeometrieDatenPunktListe();

for (i = 0; i < ni; i++)

{

GeoKoordinate GK;

GK.Laenge_grad = S.siteVector->SID_VECTOR[i].SID_int_DECIMAL;

GK.Breite_grad = S.siteVector->SID_VECTOR[i].SID_LAT_DECIMAL;

((GeometrieDatenPunktListe)GBs).PL.PL.push_back(GK);

}

((GeometrieDatenPunktListe)GBs).r_m = 0.0;

break;

case SiteVector::Type::open:

ni = S.siteVector->SID_VECTOR.size();

GBs = new GeometrieDatenPolygon();

for (i = 0; i < ni; i++)

{

GeoKoordinate GK;

GK.Laenge_grad = S.siteVector->SID_VECTOR[i].SID_int_DECIMAL;

GK.Breite_grad = S.siteVector->SID_VECTOR[i].SID_LAT_DECIMAL;

((GeometrieDatenPolygon)GBs).PL.PL.push_back(GK);

}

break;

}

}

Geometrie Gs;

Gs.pSDB = GBs;

for (g = 0; g < n_g; g++)

{

Station G = stationen.G.STATION[g];

GeometrieBasis* GBg = NULL;

if (G.sitePoint != NULL)

{

GBg = new GeometrieDatenKreis();

((GeometrieDatenKreis)GBg).S.Laenge_grad = G.sitePoint->SID_int_DECIMAL;

((GeometrieDatenKreis)GBg).S.Breite_grad = G.sitePoint->SID_LAT_DECIMAL;

((GeometrieDatenKreis)GBg).r_m = 0.0;

}

else if (G.siteCircle != NULL)

{

GBg = new GeometrieDatenKreis();

((GeometrieDatenKreis)GBg).S.Laenge_grad = G.siteCircle->SID_int_DECIMAL;

((GeometrieDatenKreis)GBg).S.Breite_grad = G.siteCircle->SID_LAT_DECIMAL;

((GeometrieDatenKreis)GBg).r_m = G.siteCircle->ST_AREA_RADIUS;

}

else if (G.siteVector != NULL)

{

switch (G.siteVector->SID_TYPE)

{

case SiteVector::Type::closed:

ni = G.siteVector->SID_VECTOR.size();

GBs = new GeometrieDatenKontur();

for (i = 0; i < ni; i++)

{

GeoKoordinate GK;

GK.Laenge_grad = G.siteVector->SID_VECTOR[i].SID_int_DECIMAL;

GK.Breite_grad = G.siteVector->SID_VECTOR[i].SID_LAT_DECIMAL;

((GeometrieDatenKontur)GBg).PL.PL.push_back(GK);

}

break;

case SiteVector::Type::list:

ni = G.siteVector->SID_VECTOR.size();

GBs = new GeometrieDatenPunktListe();

for (i = 0; i < ni; i++)

{

GeoKoordinate GK;

GK.Laenge_grad = G.siteVector->SID_VECTOR[i].SID_int_DECIMAL;

GK.Breite_grad = G.siteVector->SID_VECTOR[i].SID_LAT_DECIMAL;

((GeometrieDatenPunktListe)GBg).PL.PL.push_back(GK);

}

((GeometrieDatenPunktListe)GBs).r_m = 0.0;

break;

case SiteVector::Type::open:

ni = G.siteVector->SID_VECTOR.size();

GBs = new GeometrieDatenPolygon();

for (i = 0; i < ni; i++)

{

GeoKoordinate GK;

GK.Laenge_grad = G.siteVector->SID_VECTOR[i].SID_int_DECIMAL;

GK.Breite_grad = G.siteVector->SID_VECTOR[i].SID_LAT_DECIMAL;

((GeometrieDatenPolygon)GBg).PL.PL.push_back(GK);

}

break;

}

}

Geometrie Gg;

Gg.pSDB = GBg;

stationen.Abstandsmatrix[s][g] = Gs * Gg;

}

}

}

Best wishes

Uli

image001.jpg

While I’m not sure, this looks like a constructor for the Station class. Probably as part of:

for (s = 0; s < n_s; s++)
{
Station S = stationen.S.STATION[s];   << the copy constructor is getting called here
GeometrieBasis* GBs = NULL;

How are Station’s constructors defined? I’m wondering if they aren’t presented in a way that compiler can auto create the device versions.

-Mat

Hi Mat,

The station is defined by:

#ifndef Station

#define Station

#include “Receiver.h”

#include “Transmitter.h”

#include “…/GeoCalculator/Geometrie.h”

#include

#include

class SitePoint;

class SiteCircle;

class SiteVector;

class MY_LIB_PUBLIC Station

{

public: enum SiteType { point, circle, vector };

public:

Station();

Station(SiteType ST);

Station(const Station& toCopy);

virtual ~Station();

Station& operator =(const Station& av);

public:

void ErzeugeGeometrie();

protected:

void copyAttributes(const Station& av);

void clear();

public:

std::string ST_REF;

std::string ST_NAME;

std::string ST_CALLSIGN;

std::vector Transmitters;

std::vector Receivers;

SitePoint* sitePoint;

SiteCircle* siteCircle;

SiteVector* siteVector;

std::string SID_DESC;

std::string SID_COUNTRY;

public:

Geometrie* G;

};

#endif

In the mean time I have found a way to make the code compile and link:

If I don’t use variables with types like “Station” in the static method called by the loop then this error message doesn’t occur anymore.

But it is allowed if it is a member of the object which is the in-parameter of that method.

But I don’t understand why types like that should not be allowed here but be allowed there.

Best regards

Still just guessing, but we don’t yet support virtual functions on the device, so it’s possible that the missing symbol is coming from the destructor. Does the destructor need to be virtual?

Hi Mat,

Thanks a lot. That was the real problem.

I have skipped out all “virtual” and now it is compiling and linking well.

Perfect.

Best regards

Uli

image002.jpg