带有pthread的C ++
在我的机器上,不到1分钟的时间便达到了n = 14。但是,由于那只是2核笔记本电脑,我希望8核测试机能够在2分钟内完成n = 15。我的机器大约需要4:20分钟。
我真的很想提出一个更有效的方法。必须有一种方法可以更有效地计算二进制矩阵的确定性。我想提出一种动态编程方法,该方法在行列式计算中计算+1和-1项。但是到目前为止,它还没有完全融合在一起。
由于赏金即将到期,因此我实施了标准的暴力手段:
- 遍历所有可能的Toeplitz矩阵。
- 跳过每个转置矩阵对中的两个之一。由于矩阵是由位掩码值描述的,因此通过跳过位掩码的倒数小于位掩码本身的所有值,可以轻松做到这一点。
- 通过教科书LR分解计算确定值。除了一些小的性能调整外,我从大学数值方法书中对该算法所做的主要改进是,我使用了一种更简单的枢轴策略。
- 并行化是通过pthreads完成的。仅对每个线程处理的值使用常规间距会导致非常差的负载平衡,因此我引入了一些麻烦。
我在Mac OS上进行了测试,但之前在Ubuntu上使用过类似的代码,因此我希望它可以编译并顺利运行:
- 使用以下命令将代码保存到文件中 .cpp扩展名,例如optim.cpp。
- 编译 gcc -Ofast optim.cpp -lpthread -lstdc++。
- 用运行time ./a.out 14 8。第一个参数是最大值n。14肯定会在2分钟内完成,但是如果您也尝试15的话,那将是很好的选择。第二个参数是线程数。通常,使用与计算机内核数相同的值是一个不错的开始,但是尝试一些更改可能会缩短时间。
让我知道您在构建或运行代码时是否遇到任何问题。
#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;
}