Pthreads ile C ++
Bu, makinemde 1 dakikadan kısa bir sürede n = 14 olur. Ancak bu sadece 2 çekirdekli bir dizüstü bilgisayar olduğundan, 8 çekirdekli test makinesinin n = 15'i 2 dakikadan kısa sürede bitirebileceğini umuyorum. Makinemde yaklaşık 4:20 dakika sürüyor.
Gerçekten daha verimli bir şey bulmayı umuyordum. Orada olan var daha verimli bir ikili matris bizim tanımlı hesaplamak için bir yol olarak. Determinant hesaplamada +1 ve -1 terimlerini sayan bir tür dinamik programlama yaklaşımı bulmak istedim. Ama şimdiye kadar pek bir araya gelmedi.
Ödülün süresi dolmak üzereyken, standart kaba kuvvet yaklaşımını uyguladım:
- Tüm olası Toeplitz matrislerini döngüye sokun.
- Her bir transpoze edilmiş matris çiftindeki ikisinden birini atlayın. Matris bitmask değerleri ile tanımlandığından, bitmaskinin tersinin bitmaskinin kendisinden daha küçük olduğu tüm değerleri atlayarak bunu yapmak kolaydır.
- Determinat, bir metin kitabı LR ayrışması ile hesaplanır. Bazı küçük performans ayarlamaları dışında, kolej sayısal yöntemler kitabımdan algoritmaya yaptığım temel gelişme, daha basit bir pivot stratejisi kullanmamdır.
- Paralelleştirme pthreads ile yapılır. Sadece her bir iş parçacığı tarafından işlenen değerler için düzenli boşluk kullanmak çok kötü yük dengelemesine neden oldu, bu yüzden biraz dalgalanma getirdim.
Bunu Mac OS'de test ettim, ancak daha önce Ubuntu'da benzer bir kod kullandım, bu yüzden bu derleme ve sorunsuz bir şekilde çalışacağını umuyorum:
- Kodu,
.cpp
uzantısı olan bir dosyaya kaydedin , örn optim.cpp
.
- İle derleyin
gcc -Ofast optim.cpp -lpthread -lstdc++
.
- İle çalıştırın
time ./a.out 14 8
. İlk argüman maksimumdur n
. 14 2 dakikadan kısa sürede bitirmeli, ancak 15'i de denemeniz harika olurdu. İkinci argüman iş parçacığı sayısıdır. Makinenin çekirdek sayısı ile aynı değeri kullanmak normalde iyi bir başlangıçtır, ancak bazı varyasyonları denemek zamanları iyileştirebilir.
Kodu oluştururken veya çalıştırırken herhangi bir sorun yaşarsanız bize bildirin.
#include <stdint.h>
#include <pthread.h>
#include <cstdlib>
#include <iostream>
static int NMax = 14;
static int ThreadCount = 4;
static pthread_mutex_t ThreadMutex;
static pthread_cond_t ThreadCond;
static int BarrierCount = 0;
static float* MaxDetA;
static uint32_t* MaxDescrA;
static inline float absVal(float val)
{
return val < 0.0f ? -val : val;
}
static uint32_t reverse(int n, uint32_t descr)
{
uint32_t descrRev = 0;
for (int iBit = 0; iBit < 2 * n - 1; ++iBit)
{
descrRev <<= 1;
descrRev |= descr & 1;
descr >>= 1;
}
return descrRev;
}
static void buildMat(int n, float mat[], uint32_t descr)
{
int iDiag;
for (iDiag = 1 - n; iDiag < 0; ++iDiag)
{
float val = static_cast<float>(descr & 1);
descr >>= 1;
for (int iRow = 0; iRow < n + iDiag; ++iRow)
{
mat[iRow * (n + 1) - iDiag] = val;
}
}
for ( ; iDiag < n; ++iDiag)
{
float val = static_cast<float>(descr & 1);
descr >>= 1;
for (int iCol = 0; iCol < n - iDiag; ++iCol)
{
mat[iCol * (n + 1) + iDiag * n] = val;
}
}
}
static float determinant(int n, float mat[])
{
float det = 1.0f;
for (int k = 0; k < n - 1; ++k)
{
float maxVal = 0.0f;
int pk = 0;
for (int i = k; i < n; ++i)
{
float q = absVal(mat[i * n + k]);
if (q > maxVal)
{
maxVal = q;
pk = i;
}
}
if (pk != k)
{
det = -det;
for (int j = 0; j < n; ++j)
{
float t = mat[k * n + j];
mat[k * n + j] = mat[pk * n + j];
mat[pk * n + j] = t;
}
}
float s = mat[k * n + k];
det *= s;
s = 1.0f / s;
for (int i = k + 1; i < n; ++i)
{
mat[i * n + k] *= s;
for (int j = k + 1; j < n; ++j)
{
mat[i * n + j] -= mat[i * n + k] * mat[k * n + j];
}
}
}
det *= mat[n * n - 1];
return det;
}
static void threadBarrier()
{
pthread_mutex_lock(&ThreadMutex);
++BarrierCount;
if (BarrierCount <= ThreadCount)
{
pthread_cond_wait(&ThreadCond, &ThreadMutex);
}
else
{
pthread_cond_broadcast(&ThreadCond);
BarrierCount = 0;
}
pthread_mutex_unlock(&ThreadMutex);
}
static void* threadFunc(void* pData)
{
int* pThreadIdx = static_cast<int*>(pData);
int threadIdx = *pThreadIdx;
float* mat = new float[NMax * NMax];
for (int n = 1; n <= NMax; ++n)
{
uint32_t descrRange(1u << (2 * n - 1));
float maxDet = 0.0f;
uint32_t maxDescr = 0;
uint32_t descrInc = threadIdx;
for (uint32_t descrBase = 0;
descrBase + descrInc < descrRange;
descrBase += ThreadCount)
{
uint32_t descr = descrBase + descrInc;
descrInc = (descrInc + 1) % ThreadCount;
if (reverse(n, descr) > descr)
{
continue;
}
buildMat(n, mat, descr);
float det = determinant(n, mat);
if (det > maxDet)
{
maxDet = det;
maxDescr = descr;
}
}
MaxDetA[threadIdx] = maxDet;
MaxDescrA[threadIdx] = maxDescr;
threadBarrier();
// Let main thread output results.
threadBarrier();
}
delete[] mat;
return 0;
}
static void printMat(int n, float mat[])
{
for (int iRow = 0; iRow < n; ++iRow)
{
for (int iCol = 0; iCol < n; ++iCol)
{
std::cout << " " << mat[iRow * n + iCol];
}
std::cout << std::endl;
}
std::cout << std::endl;
}
int main(int argc, char* argv[])
{
if (argc > 1)
{
NMax = atoi(argv[1]);
if (NMax > 16)
{
NMax = 16;
}
}
if (argc > 2)
{
ThreadCount = atoi(argv[2]);
}
MaxDetA = new float[ThreadCount];
MaxDescrA = new uint32_t[ThreadCount];
pthread_mutex_init(&ThreadMutex, 0);
pthread_cond_init(&ThreadCond, 0);
int* threadIdxA = new int[ThreadCount];
pthread_t* threadA = new pthread_t[ThreadCount];
for (int iThread = 0; iThread < ThreadCount; ++iThread)
{
threadIdxA[iThread] = iThread;
pthread_create(threadA + iThread, 0, threadFunc, threadIdxA + iThread);
}
float* mat = new float[NMax * NMax];
for (int n = 1; n <= NMax; ++n)
{
threadBarrier();
float maxDet = 0.0f;
uint32_t maxDescr = 0;
for (int iThread = 0; iThread < ThreadCount; ++iThread)
{
if (MaxDetA[iThread] > maxDet)
{
maxDet = MaxDetA[iThread];
maxDescr = MaxDescrA[iThread];
}
}
std::cout << "n = " << n << " det = " << maxDet << std::endl;
buildMat(n, mat, maxDescr);
printMat(n, mat);
threadBarrier();
}
delete[] mat;
delete[] MaxDetA;
delete[] MaxDescrA;
delete[] threadIdxA;
delete[] threadA;
return 0;
}