多线程矩阵乘法ASM实现偶发结果矩阵首行元素为0问题求助
多线程矩阵乘法ASM实现偶发第一行部分元素为0问题
我正在实现多线程矩阵乘法应用,整体功能几乎都正常,唯独ASM中的多线程逻辑存在问题。当我在乘法运算阶段启用多线程时,偶发结果矩阵第一行的部分元素为0。我尝试了所有可能的方案都没能找到错误,因此向各位求助。
首先是运行正常的C++实现代码:
void multiply(int* resultRow, int* row, int* column, int size) { int* startCol = column; int* start = row; for (int i = 0; i < size; i++) { column = startCol; column += i; (*resultRow) = 0; row = start; for (int j = 0; j < size; j++) { (*resultRow) += ((*row) * (*column)); row++; column += size; } resultRow++; } }
该函数即使在多线程场景下也可以正常运行(resultRow是结果矩阵第i行的地址,row和columns是待乘矩阵对应行和列的地址)
以下是存在问题的ASM代码:
.CODE ;------------------------------------------------------------------------- ;------------------------------------------------------------------------- AsmMultiplication PROC loopCount: qword, secondLoopCount: qword, startColAddress : qword, startRowAddress : qword, count : qword, matrixSize : qword ; resultRow in RCX ; rowToMultiply in RDX ; colToMultiply in R8 ; size int R9 mov matrixSize, R9 mov loopCount, R9 mov secondLoopCount, R9 mov count, 0 mov R10, RDX mov R9, RCX mov startColAddress, R8 mov startRowAddress, R10 loop1: mov R8, startColAddress ; column = startColAddress mov R10, startRowAddress ; row = startRowAddress mov RAX, count ; | mov RCX, 4 ; | mul RCX ; | add R8, RAX ; | column += i xor RAX, RAX ; | mov [R9], RAX ; (*resultRow) = 0 mov RAX, matrixSize ; | mov loopCount, RAX ; | pxor xmm2, xmm2 ; | preparing for multiplying in loop2 inc count loop2: movq xmm0, qword ptr [R10] ;move actual row element to vector movq xmm1, qword ptr [R8] ;move actual column element to vector pmuludq xmm0, xmm1 ;multiply vectors paddq xmm2, xmm0 ;add result to third vector add R10, 4 ; row++ mov RAX, matrixSize ; | mov RDX, 4 ; | mul RDX ; | add R8, RAX ; | column += size mov RDX, loopCount ; | dec RDX ; | decrementing loop counter mov loopCount, RDX ; | jnz loop2 ; | if loopCount == 0 break movq RAX, xmm2 ; | mov [R9], RAX ; | resultRow = rows * columns add R9, 4 ; | resultRows++ mov RDX, secondLoopCount ; | dec RDX ; | mov secondLoopCount, RDX ; | jnz loop1 ; | if secondLoopCount == 0 break ret AsmMultiplication ENDP end
多线程调用逻辑如下:
public void ThreadedFunction(int size, int rows) { unsafe { fixed (int* resultRow = &m3.matrix[rows, 0]) fixed (int* rowToMultiply = &m1.matrix[rows, 0]) fixed (int* colToMultiply = &m2.matrix[0, 0]) if(Asm == false) { MatrixMultiplication.App.multiply(resultRow, rowToMultiply, colToMultiply, size); } else { MatrixMultiplication.App.AsmMultiplication(resultRow, rowToMultiply, colToMultiply, size); } } } ... for (int i = 0; i < threadsCount; i++) { threads[i] = this.StartTheThread(size, rows); rows++; }
问题原因
你的ASM代码不符合Windows x64调用约定,且临时变量存储方式不是线程安全的:
- 你在PROC声明中定义的
loopCount、secondLoopCount等伪参数,本质是栈上的共享存储位置,多线程同时调用函数时会互相覆盖这些计数器的取值,导致循环提前终止,最终输出值为初始的0 - 你使用的
R10、XMM2属于x64调用约定中的非易失性寄存器,你没有在函数开头做入栈保存、返回前出栈恢复的操作,会破坏调用方上下文,不同线程的寄存器值也会互相干扰 - C++版本正常是因为编译器会自动为每个线程的函数调用分配独立栈帧,自动处理非易失性寄存器的保存恢复,所有临时变量、计数器都是线程独立的,不存在互相覆盖的问题
修复方案
- 函数开头按照x64调用约定分配独立栈帧,把循环计数器、临时变量存在当前函数栈帧的独立偏移位置,不要用共享的伪参数存储
- 把用到的非易失性寄存器(R10、XMM2等)在函数开头入栈保存,返回前弹出恢复原有值
- 优先用寄存器存储循环计数器,避免内存读写的线程冲突
内容的提问来源于stack exchange,提问作者Tom5821
相关产品推荐
相关产品推荐

