C ++ với pthread
Điều này được n = 14 chỉ trong chưa đầy 1 phút trên máy của tôi. Nhưng vì đó chỉ là một máy tính xách tay 2 lõi, tôi hy vọng rằng máy thử nghiệm 8 lõi có thể hoàn thành n = 15 trong dưới 2 phút. Mất khoảng 4:20 phút trên máy của tôi.
Tôi đã thực sự hy vọng để đưa ra một cái gì đó hiệu quả hơn. Hiện đã có phải là một cách để tính toán quyết tâm của một ma trận nhị phân hiệu quả hơn. Tôi muốn đưa ra một số cách tiếp cận lập trình động, tính các số hạng +1 và -1 trong phép tính xác định. Nhưng nó vẫn chưa đến được với nhau cho đến nay.
Vì tiền thưởng sắp hết hạn, tôi đã thực hiện phương pháp vũ phu tiêu chuẩn:
- Lặp lại tất cả các ma trận Toeplitz có thể.
- Bỏ qua một trong hai trong mỗi cặp ma trận chuyển vị. Vì ma trận được mô tả bởi các giá trị bitmask, điều này rất đơn giản để thực hiện bằng cách bỏ qua tất cả các giá trị trong đó độ đảo ngược của bitmask nhỏ hơn chính bitmask.
- Việc xác định được tính toán với phân tách LR sách giáo khoa. Ngoại trừ một số điều chỉnh hiệu suất nhỏ, cải tiến chính mà tôi đã thực hiện đối với thuật toán từ sách phương pháp số đại học của tôi là tôi sử dụng chiến lược xoay vòng đơn giản hơn.
- Song song được thực hiện với pthreads. Chỉ sử dụng khoảng cách thông thường cho các giá trị được xử lý bởi mỗi luồng gây ra sự cân bằng tải rất xấu, vì vậy tôi đã giới thiệu một số sự thay đổi.
Tôi đã thử nghiệm điều này trên Mac OS, nhưng tôi đã sử dụng mã tương tự trên Ubuntu trước đây, vì vậy tôi hy vọng điều này sẽ biên dịch và chạy mà không gặp trở ngại nào:
- Lưu mã trong một tệp có
.cpp
phần mở rộng, ví dụ optim.cpp
.
- Biên dịch với
gcc -Ofast optim.cpp -lpthread -lstdc++
.
- Chạy với
time ./a.out 14 8
. Đối số đầu tiên là tối đa n
. Chắc chắn 14 sẽ hoàn thành trong vòng dưới 2 phút, nhưng sẽ rất tuyệt nếu bạn có thể thử 15 như vậy. Đối số thứ hai là số lượng chủ đề. Sử dụng cùng giá trị với số lõi của máy thường là một khởi đầu tốt, nhưng thử một số biến thể có khả năng cải thiện thời gian.
Hãy cho tôi biết nếu bạn có bất kỳ vấn đề nào khi xây dựng hoặc chạy mã.
#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;
}