std::thread oraz cuda na kiju z nim związane

1

Mam pytanie co do poniższego kodu.
Już od dawna zauważyłem że std::thread pod MinGW działa tak jak jemu się chce, ale takie cuda to po raz pierwszy widzę.
Czemu poniższy kod przy ustawieniu: const unsigned ThreadCount=8; - działa poprawnie
zaś po przestawieniu: const unsigned ThreadCount=2; - wywala się z wyjątkiem, no chyba że włączę debuger :)

#include <iostream>
#include <thread>
using namespace std;
 
#define USE_THREAD
//
#define USE_DOUBLE
 
const unsigned PairCount=18;
const unsigned ThreadCount=2; // Przy 8 - działa poprawnie
const unsigned FiltrCount=64;
const unsigned DeadCount=128;
const unsigned ForwardCount=128;
const unsigned ResultCount=8;
const unsigned SavedCount=DeadCount+ForwardCount+ResultCount;
const unsigned ColumnCount=FiltrCount*2;
 
#ifdef USE_DOUBLE
struct MatrixData
  {
   double X[PairCount];
   // and more ...
  };
MatrixData SV[SavedCount+1]={{0}};
 
double TC[PairCount][ColumnCount][ColumnCount]={{{0}}};
double TR[PairCount][ColumnCount]={0};
double TY[ResultCount][ColumnCount][PairCount]={{{0}}};
#else
template<class T> struct VirtualTab
  {
   unsigned Size;
   T value;
   VirtualTab(unsigned Size,const T &value):Size(Size),value(value) { }
   T &operator[](unsigned p)
     {
      if(p>=Size)
        {
         cout<<"A tu cie mam"<<endl;
         cin.get();
        }
      return value;
     }
  };
 
struct MatrixData
  {
   VirtualTab<double> X;
   // and more ...
   MatrixData():X(PairCount,0) {}
  };
 
VirtualTab<MatrixData> SV(SavedCount+1,MatrixData());
 
VirtualTab<VirtualTab<VirtualTab<double> > > TC(PairCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(ColumnCount,0)));
VirtualTab<VirtualTab<double> > TR(PairCount,VirtualTab<double>(ColumnCount,0));
VirtualTab<VirtualTab<VirtualTab<double> > > TY(ResultCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(PairCount,0)));
#endif
 
void proc(int t)
  {
   for(unsigned y=t;y<ColumnCount;y+=ThreadCount)
     {
      for(unsigned p=0;p<PairCount;++p)
        {
         double add=0;
#ifdef USE_DOUBLE
         double *tcy=TC[p][y];
#else
         VirtualTab<double> &tcy=TC[p][y];
#endif
         for(unsigned x=0;x<ColumnCount;++x)
           {
            add+=tcy[x]*TR[p][x];
           }
         for(unsigned s=0;s<ResultCount;++s)
           {
            TY[s][y][p]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);
           }
        }
     }
  }
 
int main()
  {
#ifdef USE_THREAD
            thread th[ThreadCount];
#endif
   unsigned RowCount=0;
   while(RowCount<1024*1024*1024)
     {
            for(unsigned t=0;t<ThreadCount;++t)
              {
#ifdef USE_THREAD
               th[t]=thread(proc,t);
#else
			   proc(t);
#endif
              }
            if(!(++RowCount&0xFF))
              {
               cout<<'\r'<<RowCount;
               cout.flush();
              }
#ifdef USE_THREAD
            for(unsigned t=0;t<ThreadCount;++t) th[t].join();
#endif
     }
   return 0;
  }
1

Na dwóch wątkach nawet pierwsze cout<<'\r'<<RowCount; się nie odpala

Linux, GCC 4.9.1 - wydaje się działać.

Implementacja na Windowsie może być jakaś szalona, jesteś zmuszony używać MinGW? Niedawno używałem wszystkich najnowszych ficzerów C++11 (i 14) w VS 2013 - wszystko było ok. Poza tym już kiedyś pisałem o tym w podobnym Twoim temacie - MinGW ma kilka buildów, może któryś zadziała? To takie smutne. :-(

(Tag <quote> się tu wysypuje :-D)

1
  1. jaki komunikat?
  2. czy da się przechwycić?
  3. jeśli tak, to jakie są wartości y,p i RowCount?

Edit: dla aktualnego CodeBlocks (13) + TDM-GCC-481 wszystko działa OK na dwóch wątkach.
Przerwałem na 100000.

1

Coś dziwnie działają te wątki - pewnie problem z cachem:

#include <iostream>
#include <thread>
using namespace std;

//#define USE_THREAD
//
#define USE_DOUBLE

const unsigned PairCount=18;
const unsigned ThreadCount=2; // Przy 8 - dzia³a poprawnie
const unsigned FiltrCount=64;
const unsigned DeadCount=128;
const unsigned ForwardCount=128;
const unsigned ResultCount=8;
const unsigned SavedCount=DeadCount+ForwardCount+ResultCount;
const unsigned ColumnCount=FiltrCount*2;

#ifdef USE_DOUBLE
struct MatrixData
{
    double X[PairCount];
    // and more ...
};
MatrixData SV[SavedCount+1]= {{0}};

double TC[PairCount][ColumnCount][ColumnCount]= {{{0}}};
double TR[PairCount][ColumnCount]= {0};
double TY[ResultCount][ColumnCount][PairCount]= {{{0}}};
#else
template<class T> struct VirtualTab
{
    unsigned Size;
    T value;
    VirtualTab(unsigned Size,const T &value):Size(Size),value(value) { }
    T &operator[](unsigned p)
    {
        if(p>=Size)
        {
            cout<<"A tu cie mam"<<endl;
            cin.get();
        }
        return value;
    }
};

struct MatrixData
{
    VirtualTab<double> X;
    // and more ...
    MatrixData():X(PairCount,0) {}
};

VirtualTab<MatrixData> SV(SavedCount+1,MatrixData());

VirtualTab<VirtualTab<VirtualTab<double> > > TC(PairCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(ColumnCount,0)));
VirtualTab<VirtualTab<double> > TR(PairCount,VirtualTab<double>(ColumnCount,0));
VirtualTab<VirtualTab<VirtualTab<double> > > TY(ResultCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(PairCount,0)));
#endif

void proc(int t)
{
    for(unsigned y=t; y<ColumnCount; y+=ThreadCount)
    {
        for(unsigned p=0; p<PairCount; ++p)
        {
            double add=0;
#ifdef USE_DOUBLE
            double *tcy=TC[p][y];
#else
            VirtualTab<double> &tcy=TC[p][y];
#endif
            for(unsigned x=0; x<ColumnCount; ++x)
            {
                add+=tcy[x]*TR[p][x];
            }
            for(unsigned s=0; s<ResultCount; ++s)
            {
                TY[s][y][p]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);
            }
        }
    }
}

#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int64 uint64;
#else
typedef unsigned long long uint64;
#endif

typedef uint64 cpu_ticks;

double cpu_time()
{
  double res;
  res = (double) clock() / (double) CLOCKS_PER_SEC;
  return res;
}

cpu_ticks cpu_time_ms()
{
  return (cpu_ticks)(cpu_time()*1000.0);
}

int main()
{
#ifdef USE_THREAD
    thread th[ThreadCount];
#endif
    unsigned RowCount=0;

    cpu_ticks startTime = cpu_time_ms();
    cpu_ticks stepTime;

    while(RowCount<1024*1024*1024)
    {
        for(unsigned t=0; t<ThreadCount; ++t)
        {
#ifdef USE_THREAD
            th[t]=thread(proc,t);
#else
            proc(t);
#endif
        }
        if(!(++RowCount&0xFFF))
        {
            stepTime = cpu_time_ms() - startTime;
            cout<<'\r'<<RowCount<<", stepTime: "<<stepTime << endl;
            cout.flush();
            startTime = cpu_time_ms();
        }
#ifdef USE_THREAD
        for(unsigned t=0; t<ThreadCount; ++t) th[t].join();
#endif
    }
    return 0;
}

Na 6 rdzeniach:

6 threads
stepTime: 2449, CPU: 51%

2 threads
stepTime: 2028, CPU: 17%

no threads
stepTime: 1888, CPU: 18%

0

Nie wiem dokładnie jak działa ten program, ale po zmianie:

double TY[ResultCount][ColumnCount][PairCount]= {{{0}}};

na:

double TY[PairCount][ColumnCount][ResultCount]= {{{0}}};

oraz

TY[s][y][p]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);

na

TY[p][y][s]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);

Przyspieszyło nieznacznie: stepTime: 2574 (na 6 wątkach).

0

Sprawdziłem dokładniej - drugie przypuszczenie było prawdziwe.
Gdy dołoży się pracy w jednym wykonaniu wątku, wydajność skacze i obciążenie CPU też.

#include <iostream>
#include <thread>
using namespace std;

#define USE_THREAD
//
#define USE_DOUBLE

const unsigned PairCount=18;
const unsigned ThreadCount=6; // Przy 8 - dzia³a poprawnie
const unsigned FiltrCount=64;
const unsigned DeadCount=128;
const unsigned ForwardCount=128;
const unsigned ResultCount=8;
const unsigned SavedCount=DeadCount+ForwardCount+ResultCount;
const unsigned ColumnCount=FiltrCount*2;
const int IntStep = 32;

#ifdef USE_DOUBLE
struct MatrixData
{
    double X[PairCount];
    // and more ...
};
MatrixData SV[SavedCount+1]= {{0}};

double TC[PairCount][ColumnCount][ColumnCount]= {{{0}}};
double TR[PairCount][ColumnCount]= {0};
double TY[ResultCount][ColumnCount][PairCount]= {{{0}}};
#else
template<class T> struct VirtualTab
{
    unsigned Size;
    T value;
    VirtualTab(unsigned Size,const T &value):Size(Size),value(value) { }
    T &operator[](unsigned p)
    {
        if(p>=Size)
        {
            cout<<"A tu cie mam"<<endl;
            cin.get();
        }
        return value;
    }
};

struct MatrixData
{
    VirtualTab<double> X;
    // and more ...
    MatrixData():X(PairCount,0) {}
};

VirtualTab<MatrixData> SV(SavedCount+1,MatrixData());

VirtualTab<VirtualTab<VirtualTab<double> > > TC(PairCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(ColumnCount,0)));
VirtualTab<VirtualTab<double> > TR(PairCount,VirtualTab<double>(ColumnCount,0));
VirtualTab<VirtualTab<VirtualTab<double> > > TY(ResultCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(PairCount,0)));
#endif

void proc(int t)
{
    for(int st = 0; st < IntStep; ++st)
    for(unsigned y=t; y<ColumnCount; y+=ThreadCount)
    {
        for(unsigned p=0; p<PairCount; ++p)
        {
            double add=0;
#ifdef USE_DOUBLE
            double *tcy=TC[p][y];
#else
            VirtualTab<double> &tcy=TC[p][y];
#endif
            for(unsigned x=0; x<ColumnCount; ++x)
            {
                add+=tcy[x]*TR[p][x];
            }
            for(unsigned s=0; s<ResultCount; ++s)
            {
                TY[s][y][p]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);
            }
        }
    }
}

#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef unsigned __int64 uint64;
#else
typedef unsigned long long uint64;
#endif

typedef uint64 cpu_ticks;

double cpu_time()
{
  double res;
  res = (double) clock() / (double) CLOCKS_PER_SEC;
  return res;
}

cpu_ticks cpu_time_ms()
{
  return (cpu_ticks)(cpu_time()*1000.0);
}

int main()
{
#ifdef USE_THREAD
    thread th[ThreadCount];
#endif
    unsigned RowCount=0;

    cpu_ticks startTime = cpu_time_ms();
    cpu_ticks stepTime;

    while(RowCount<1024*1024*1024)
    {
        for(unsigned t=0; t<ThreadCount; t+=1)
        {
#ifdef USE_THREAD
            th[t]=thread(proc,t);
#else
            proc(t);
#endif
        }
        if(!(++RowCount&0xFFF))
        {
            stepTime = cpu_time_ms() - startTime;
            cout<<'\r'<<RowCount<<", stepTime: "<<stepTime/IntStep << endl;
            cout.flush();
            startTime = cpu_time_ms();
        }
#ifdef USE_THREAD
        for(unsigned t=0; t<ThreadCount; ++t) th[t].join();
#endif
    }
    return 0;
}

Wyniki z wcześniejszą "antyoptymalizacją":

IntStep = 32, threads = 6
=> stepTime: 669, CPU = 95%

IntStep = 8, threads = 6
=> stepTime: 836, CPU = 85%

IntStep = 4, threads = 6
=> stepTime: 1045, CPU = 74%

IntStep = 2, threads = 6
=> stepTime: 1427, CPU = 67%

IntStep = 1, threads = 6
=> stepTime: 2527, CPU = 53%

Wyniki dla kodu z tego postu:

IntStep = 32, threads = 6
=> stepTime: 473, CPU = 92%

IntStep = 128, threads = 6
=> stepTime: 440, CPU = 93%

Podsumowując:
Zwiększając nakład pracy dla jednego wykonania wątku można przyspieszyć proces co najmniej 6x.

1 użytkowników online, w tym zalogowanych: 0, gości: 1