【问题标题】:matrix multiplication in a multithreading way多线程方式的矩阵乘法
【发布时间】:2020-06-25 15:51:33
【问题描述】:

您好,我尝试通过以下练习自学多线程:将两个硬编码矩阵矩阵 1 和矩阵 2 相乘,并将结果保存在矩阵 3 中。策略是分配一个线程来计算结果矩阵(matrix3)中的每个元素。

虽然程序是可以运行的,但是结果是不对的。任何启示为什么结果是错误的?谢谢!

代码

#include <stdio.h>
#include <stdlib.h>

#include "thread.h"

void *multiply(void *arguments);

#define X 2
#define Y 2
#define Z 3

int matrix1[X][Y] = {{0, 1}, {2, 3}};
int matrix2[Y][Z] = {{4, 5, 6}, {7, 8, 9}};
int matrix3[X][Z];

struct arguments_combo
{
    int rowIdxInMatrix1;
    int colIdxInMatrix2;
};

int main()
{
    thread_t threads[X * Z];

    for (int rowIdx = 0; rowIdx < X; rowIdx++)
    {
        for (int colIdx = 0; colIdx < Z; colIdx++)
        {
            struct arguments_combo arguments;
            arguments.rowIdxInMatrix1 = rowIdx;
            arguments.colIdxInMatrix2 = colIdx;
            pthread_create(&(threads[rowIdx * Z + colIdx]), NULL, multiply, (void *)&arguments);
        }
    }

    for (int rowIdx = 0; rowIdx < X; rowIdx++)
    {
        for (int colIdx = 0; colIdx < Z; colIdx++)
        {
            void *val_ptr;
            pthread_join(threads[rowIdx * Z + colIdx], &val_ptr);
            int *cur_val_ptr = (int *)val_ptr;
            printf("The rowIdx: %d and colIdx: %d and the val: %d\n", rowIdx, colIdx, *cur_val_ptr);
            matrix3[rowIdx][colIdx] = *cur_val_ptr;
            free(cur_val_ptr);
        }
    }

    return 0;
}

void *multiply(void *arguments)
{
    struct arguments_combo *data_ptr = (struct arguments_combo *)arguments;

    int rowIdxInMatrix1 = data_ptr->rowIdxInMatrix1;
    int colIdxInMatrix2 = data_ptr->colIdxInMatrix2;
    int *sum = malloc(sizeof(int));

    for (int i = 0; i < Y; i++)
    {
        *sum += matrix1[rowIdxInMatrix1][i] * matrix2[i][colIdxInMatrix2];
    }

    pthread_exit((void *)sum);
    return NULL;
}

输出

The rowIdx: 0 and colIdx: 0 and the val: 9
The rowIdx: 0 and colIdx: 1 and the val: 29
The rowIdx: 0 and colIdx: 2 and the val: 29
The rowIdx: 1 and colIdx: 0 and the val: 34
The rowIdx: 1 and colIdx: 1 and the val: 39
The rowIdx: 1 and colIdx: 2 and the val: 39

【问题讨论】:

  • 也许一开始就去掉线程,直到你有一个工作的串行版本。然后将其转换为线程版本。 “过早的优化是万恶之源”。
  • 顺便说一句,为什么要使用 void *?你不应该使用类型系统来帮助你吗?
  • 我认为 Strassen mutrix 乘法算法已经到位。您可以使用该算法来降低时间复杂度。它基于并行处理。
  • 这里使用多线程的重点只是为了练习而不是优化矩阵乘法效率。但是,是的,我喜欢这句话“过早的优化是万恶之源”:)。无论如何,谢谢你们的cmets!

标签: c multithreading


【解决方案1】:

好吧,我不是这个主题的专家,只是试图纠正它。似乎原因是这部分

struct arguments_combo arguments;
arguments.rowIdxInMatrix1 = rowIdx;
arguments.colIdxInMatrix2 = colIdx;
pthread_create(&(threads[rowIdx * Z + colIdx]), NULL, multiply, (void *)&arguments);

可能是错误的,但我认为这里存在竞争条件。您将这个单一的arguments 变量地址提供给所有线程,但您稍后也会覆盖它,因此当访问它的线程可能已经具有其他值时。

如果您在全局范围内创建像 struct arguments_combo args[X * Z]; 这样的 args 数组,并且您也将代码更改为:

args[rowIdx * Z + colIdx].rowIdxInMatrix1 = rowIdx;
args[rowIdx * Z + colIdx].colIdxInMatrix2 = colIdx;
pthread_create(&(threads[rowIdx * Z + colIdx]), NULL, multiply, (void *)&args[rowIdx * Z + colIdx]);

那么结果在这种情况下似乎是有效的

The rowIdx: 0 and colIdx: 0 and the val: 7
The rowIdx: 0 and colIdx: 1 and the val: 8
The rowIdx: 0 and colIdx: 2 and the val: 9
The rowIdx: 1 and colIdx: 0 and the val: 29
The rowIdx: 1 and colIdx: 1 and the val: 34
The rowIdx: 1 and colIdx: 2 and the val: 39

虽然有时它会给出随机值。原因是您没有在 multiply 函数中将 *sum 初始化为 0,所以不要忘记也这样做,而且一切似乎都工作正常。

【讨论】:

  • 哦,这很有帮助,感谢您的分享!还有两个问题:1.为什么变量arguments地址在for循环的不同迭代中是一样的? 2. 能否请您详细解释一下*sum 初始化?它应该发生在我打电话给malloc 之前还是之后?太感谢了! :)
  • @RoseBoy 2) 在 malloc 之后 1) 因为它将分配在堆栈上的相同地址上......它只是......堆栈你知道的东西。不是我知道的最可靠的答案。 godbolt.org/z/XpNTar 检查这个,你会看到 for 循环体的程序集是相同的,所以实际上 k 将在每次迭代中位于相同的地址。
  • 1.是的,我运行 LLDB 进行调试,看到 arguments 的地址在不同的迭代中总是相同的......我认为一旦 arguments 在每次迭代后被“销毁”,它将在下一次迭代中的地址不同,但地址相同。 2. 8:42看这个视频(youtube.com/watch?v=9uhSYDY4sxA&t=522s) 他没有初始化指针,不知为什么
  • 如果你用一个不依赖于当前值的新值覆盖它,你不需要,但在你的代码中你像*sum += ...一样使用它,所以你实际上添加到它的当前值,所以如果如果您的代码类似于*sum = some_value,那么它就是一些垃圾值,因为您将值设置为具体的新值,所以不需要初始化它。它与指针无关,而是求和的方式,所以如果你有一个像 int sum 这样的普通变量,你也应该将它初始化为 0 以避免这个问题与 sum+= ...
  • 我明白了。这里初始化的主要原因是*sum += ...,而不是设置一个新值。非常感谢您,您对教学感到惊讶!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 2014-05-16
  • 1970-01-01
相关资源
最近更新 更多