多线程矩阵乘法
我正在编写一个使用线程级并行执行N X N矩阵乘法的代码。
得到C = A X B,
首先,我将矩阵B,分割矩阵转换成块。
一个线程从A和B中获取一个块并将它们相乘,
然后将结果添加到C中的相应块。
使用malloc()在堆内存中分配所有矩阵。
但问题是,对于相同的输入,
答案有时是不正确的,有时是正确的。
我不太确定为什么会发生这种情况,但我想代码需要在线程安全性方面进行改进。
我发布了我的部分代码。
块是行和列中的块数,即N /块大小。
所以线程的总数是块^ 3。
while (thread_loaded < total_thread)
{
if (thread_count < MAX_THREAD)
{
p[thread_loaded].idx = thread_idx;
p[thread_loaded].matinfo = &mi;
threads[thread_loaded] = CreateThread(NULL, 0, matmul_tlp, &p[thread_loaded], 0, &threadid[thread_loaded]);
if (threads[thread_loaded] != NULL)
{
thread_loaded++;
thread_count++;
thread_idx += blocks;
if (thread_idx >= total_thread)
thread_idx = (thread_idx % total_thread) + 1;
}
}
}
对于线程函数,
int i, j, k;
param* p = (param*)arg;
int blocks = BLOCKS;
int block_size = BLOCK_SIZE;
int Ar = p->idx / (blocks * blocks);
int Ac = p->idx % blocks;
int Br = (p->idx / blocks) % blocks;
int Bc = p->idx % blocks;
int Cr = p->idx / (blocks * blocks);
int Cc = (p->idx % (blocks * blocks)) / blocks;
double** A = p->matinfo->A;
double** B = p->matinfo->B;
double** C = p->matinfo->C;
DWORD res = WaitForSingleObject(mutex[Cr * blocks + Cc], INFINITE);
if (res != WAIT_OBJECT_0)
perror("cannot acquire mutex.");
for (i = 0; i < block_size; i++)
{
for (j = 0; j < block_size; j++)
{
for (k = 0; k < block_size; k++)
{
C[Cr * block_size + i][Cc * block_size + j] +=
A[Ar * block_size + i][Ac * block_size + k] *
B[Br * block_size + j][Bc * block_size + k];
}
}
}
ReleaseMutex(mutex[Cr * blocks + Cc]);
thread_count--;
return NULL;
如果有人能找到一个洞,我们将不胜感激。 :)
没有找到相关结果
已邀请:
2 个回复
鲁釜头
所以你刚刚丢失了一个信号到线程创建功能。这可能会导致算法在丢失减量时以越来越少的线程运行。可悲的是,除非我错过了什么,否则我不认为它解释了为什么你会得到不好的结果。你的程序运行速度比实际要慢。 解决这个问题的最简单方法是使用专门针对这些类型情况的条件变量,并避免您在此处遇到的丢失唤醒问题。 顺便说一下,创建线程通常是一个相当昂贵的操作,因此线程池比创建和销毁大量线程更可取。如果线程池比必要的更复杂,那么可以使用条件变量使线程在完成当前任务后等待新任务。 编辑:我写的解决方案有点太快,我最终混淆了两个不同的问题。 首先,您要在线程创建函数和计算函数中同步对thread_count变量的访问。要做到这一点,您可以使用互斥锁,也可以使用InterlockedDecrement,InterlockedIncrement等原子操作数(我希望这些是正确的Windows等价物)。请注意,虽然在这种情况下我没有看到使用原子操作数的任何问题,但它们并不像看起来那么简单。在使用它们之前,请确保完全理解它们。 第二个问题是,在等待其中一个线程完成时,您正在创建线程创建函数。您可以通过使用条件变量在主计算完成后发出信号来避免这种情况。这样你就不会无缘无故地占用处理器时间。 还有一件事,确保你的thread_count变量被声明为volatile,以向编译器指示它在函数中读取和写入的顺序很重要。否则,编译器可以自由地重新排序操作以提高性能。
疮痪徘弦漏