繁体   English   中英

使用多线程的矩阵乘法

[英]Matrix multiplication using multiple threads

所以我试图用线程计算(M × N 矩阵)次(N × 1 向量)运算到结果向量中。 我书中的问题说我应该考虑使用多少个线程,并且我假设由于结果矩阵应该是 M × 1,那么我应该使用 M 个线程,每组操作一个线程。 M 是高度,N 是宽度。

创建我使用的线程

thread* myThreads = new thread[height];

然后我多次调用 MatrixMultThreads 函数。 最后我加入了所有线程。

    for (int i = 0; i < height; i++)
    {
        myThreads[i] = thread(MatrixMultThreads, my2DArray, vector, height, width);
    }
    for (int i = 0; i < height; i++)
    {
        myThreads[i].join();
    }

我无法弄清楚的是我应该如何以正确的顺序总结所有结果值。 我将如何告诉每个特定线程该做什么。 我在想,也许我应该创建一个全局变量 step_i 并将其设置为 0,然后每次调用该函数时我都可以迭代该变量。 然后因为我可以传递数组的宽度,所以我遍历每个 step_i 并添加arr[i][j] * vector[j]

您可以将 Nx1 向量中的行点结果存储在 Mx1 向量中,然后进行求和。

顺便说一下,对于这样的问题,使用 OpenMP 会更好,它会根据机器上的内核数量自动化大部分线程管理,因为在这里您可能会产生很多线程:

https://www.openmp.org/

http://www.bowdoin.edu/~ltoma/teaching/cs3225-GIS/fall17/Lectures/openmp.html

我无法弄清楚的是我应该如何以正确的顺序总结所有结果值。

它们可以无序求和,这就是为什么这是一个用多线程解决的好问题。 如果排序对特定问题很重要,则无法使用多线程对其进行改进(需要明确的是,如果任何子问题可以乱序解决,则该子问题多线程的潜在候选者)。

解决您的问题的一种方法是在调用站点设置一个解向量,然后通过引用传递相应的元素( MatrixMultiply函数也需要知道它正在解决哪个问题):

void MatrixMultiply(const Array2d& matrix, 
       const vector<int>& vec, int row, int& solution);

// ...

vector<int> result(height);

for (int i = 0; i < height; i++)
{
    threads[i] = thread(MatrixMultiply, array2d, array1d, i, result[i]);
}

您的 2D 数组应该真正提供有关其高度和宽度的信息,而无需显式传递这些值。


奖金信息

我们可以使这个解决方案更加 OOP,您希望在将来的问题中重用(并且一些有经验的程序员似乎错过了使用数组的这个技巧):

MatrixMultiply函数与点积函数非常相似:

template <typename V1, typename V2>
auto DotProduct(const V1& vec1, const V2& vec2)
{
    auto result = vec1[0] * vec2[0];

    for (size_t i = 1; i < vec1.size(); ++i)
        result += vec1[i] * vec2[i];

    return result;
}

template <typename V1, typename V2, typename T>
auto DotProduct(const V1& vec1, const V2& vec2, T& result)
{
    result = DotProduct(vec1, vec2);
}

(上面允许向量是任何按预期使用size()[]方法的对象。)

我们可以围绕std::vector编写一个包装类,我们的数组类可以使用它来为我们处理所有索引; 像这样:

template <typename T, typename A>
class SubVector
{
    const typename std::vector<T,A>::iterator m_it;
    const size_t m_size, m_interval_size;

public:

    SubVector (std::vector<T,A>& v, size_t start, size_t sub_size, size_t i_size = 1)
        : m_it(v.begin() + start), m_size(sub_size), m_interval_size(i_size)
    {}
    
    auto size () const
    {
        return m_size;
    }
    
    const T& operator [] (size_t i) const
    {
        return it[i*m_interval_size];
    }

    T& operator [] (size_t i)
    {
        return it[i*m_interval_size];
    }
};

然后你可以在数组中的某种Vectorise方法中使用它; 像这样:

template <typename T, typename A = std::allocator<T>>
class Array2D
{
    std::vector<T,A> m_data;

    size_t m_width, m_height;

public:

    // your normal methods  

    auto VectoriseRow(int r) const
    {
        return SubVector(m_data, r*m_width, m_width);
    }

    auto VectoriseColumn(int c) const
    {
        return SubVector(m_data, c, m_height, m_width);
    }
}

(注意:我们可以通过在std::arrayboost::multi_array周围编写一个包装器来将Vectorise功能添加到它们,这使我们的数组类更通用,并使我们不必做所有的工作boost实际上有这种类型使用array_view内置的功能。)

现在我们的调用站点可以是这样的:

vector<int> result(height);

for (int i = 0; i < height; i++)
{
    threads[i] = thread(DotProduct, array2d.VectoriseRow(i), array1d, result[i]);
}

这似乎是解决原始问题的更冗长的方法(因为它确实如此),但是如果您在编码中使用多维数组,您会发现您不再需要编写特定于多数组的函数,或者处理丑陋的子问题的索引(即使是一维问题,如均值)。 在处理这类问题时,您总是希望重用类似上述代码的东西。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM