pthreadを使用したC ++
これは、私のマシンでは1分弱でn = 14になります。しかし、それは単なる2コアのラップトップであるため、8コアのテストマシンが2分以内にn = 15を終了できることを願っています。私のマシンでは約4:20分かかります。
もっと効率的なものを思いつきたいと思っていました。そこていました、より効率的にバイナリ行列の確定を計算するための方法であると。行列式の計算で+1と-1の項を数える、ある種の動的プログラミングアプローチを思いつきました。しかし、これまでのところ、まだ完全には一致していません。
報奨金はもうすぐ切れるので、標準的なブルートフォースアプローチを実装しました。
- 可能なすべてのテプリッツ行列をループします。
- 各転置行列ペアの2つのうちの1つをスキップします。マトリックスはビットマスク値で記述されるため、ビットマスクの逆がビットマスク自体よりも小さいすべての値をスキップすることにより、これを簡単に行うことができます。
- 確定値は、テキストブックLR分解で計算されます。いくつかのマイナーなパフォーマンスチューニングを除き、大学の数値手法の本からアルゴリズムに加えた主な改善点は、より単純なピボット戦略を使用することです。
- 並列化はpthreadを使用して行われます。各スレッドによって処理される値に通常の間隔を使用すると、負荷分散が非常に悪くなり、スウィズルが発生しました。
私はこれをMac OSでテストしましたが、以前Ubuntuで同様のコードを使用したため、これが問題なくコンパイルおよび実行されることを願っています。
- コードを
.cpp
拡張子付きのファイルに保存します(例:)optim.cpp
。
- でコンパイルし
gcc -Ofast optim.cpp -lpthread -lstdc++
ます。
- で実行し
time ./a.out 14 8
ます。最初の引数は最大値n
です。14は確実に2分以内に終了するはずですが、15を試してみるのもいいでしょう。2番目の引数は、スレッドの数です。マシンのコアの数と同じ値を使用することは通常良いスタートですが、いくつかのバリエーションを試してみると時間を改善できる可能性があります。
コードの構築または実行に問題がある場合はお知らせください。
#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;
}