汇编语言中矩阵乘法实现:含mul/imul/移位与无该类指令的方法
汇编中实现矩阵乘法的两种方案(含mul/imul/移位 & 无乘法指令版本)
嘿,作为常年跟汇编打交道的老鸟,我来给你拆解这两种场景的实现思路和代码示例——矩阵乘法的核心是行×列累加,不同指令限制下的实现逻辑差别挺大的:
一、使用mul/imul或移位操作实现矩阵乘法
mul/imul是汇编里原生的乘法指令,移位则是针对2的幂次乘法的性能优化(毕竟移位比乘法指令执行周期短),适合常规场景下的矩阵乘法实现。
1. 用mul/imul实现的基础示例(x86汇编,NASM风格)
假设我们处理两个2×2的带符号整数矩阵,代码框架如下:
section .data ; 2x2带符号矩阵A A dd 1, 2 dd 3, 4 ; 2x2带符号矩阵B B dd 5, 6 dd 7, 8 ; 结果矩阵C(初始化全0) C dd 0, 0 dd 0, 0 section .text global _start _start: ; 外层循环:遍历A的行(i=0,1) mov ecx, 0 ; ecx = 行索引i row_loop: cmp ecx, 2 jge end_row_loop ; 中层循环:遍历B的列(j=0,1) mov edx, 0 ; edx = 列索引j col_loop: cmp edx, 2 jge end_col_loop ; 内层循环:计算行i × 列j的累加和(k=0,1) mov ebx, 0 ; ebx = 元素索引k mov esi, 0 ; esi = 累加和(临时存C[i][j]的值) sum_loop: cmp ebx, 2 jge end_sum_loop ; 取A[i][k]和B[k][j],计算乘积后累加 mov eax, [A + ecx*8 + ebx*4] ; 取A的第i行第k个元素(每行2个dd,共8字节) imul eax, [B + ebx*8 + edx*4] ; 乘B的第k行第j个元素(带符号乘法) add esi, eax ; 累加到临时寄存器 inc ebx jmp sum_loop end_sum_loop: mov [C + ecx*8 + edx*4], esi ; 把累加结果存入C[i][j] inc edx jmp col_loop end_col_loop: inc ecx jmp row_loop end_row_loop: ; Linux系统调用退出程序 mov eax, 1 xor ebx, ebx int 0x80
- 如果是无符号矩阵,把
imul换成mul即可(mul会把无符号乘积存在edx:eax中,注意寄存器的处理); imul的立即数版本更灵活,可以直接指定结果存储的寄存器,适合复杂场景。
2. 移位操作优化乘法
如果你的矩阵元素是2的幂次(比如×2、×4),完全可以用移位指令替代乘法,比如:
; 替代 imul eax, 4 → 等价于左移2位 shl eax, 2 ; 替代 imul eax, 8 → 左移3位 shl eax, 3
移位操作的执行效率远高于mul/imul,适合对性能要求极高的场景。
二、不使用mul/imul和移位操作实现矩阵自乘
矩阵自乘即C = A×A,这种限制下只能用加法模拟乘法——毕竟乘法的本质就是重复累加。我们可以先写一个加法模拟乘法的子程序,再嵌入矩阵循环中。
加法模拟乘法的子程序(x86汇编)
; 子程序:mult_add ; 输入:eax = 乘数a,ebx = 乘数b ; 输出:eax = a×b mult_add: push ecx ; 保存现场,避免破坏外层循环的寄存器 push edx mov ecx, ebx ; ecx = 循环次数(选较小的数当次数更高效,这里简化处理) mov edx, eax ; edx = 要重复累加的数 xor eax, eax ; 初始化乘积为0 mult_loop: cmp ecx, 0 jle end_mult add eax, edx dec ecx jmp mult_loop end_mult: pop edx ; 恢复现场 pop ecx ret
矩阵自乘的主程序框架
section .data ; 2x2矩阵A A dd 1, 2 dd 3, 4 ; 结果矩阵C(初始化全0) C dd 0, 0 dd 0, 0 section .text global _start _start: mov ecx, 0 ; i = 行索引 row_loop_self: cmp ecx, 2 jge end_row_self mov edx, 0 ; j = 列索引 col_loop_self: cmp edx, 2 jge end_col_self mov ebx, 0 ; k = 元素索引 mov esi, 0 ; 累加和初始化 sum_loop_self: cmp ebx, 2 jge end_sum_self ; 取A[i][k]和A[k][j],注意保存k的值避免被覆盖 push ebx mov eax, [A + ecx*8 + ebx*4] ; 取A[i][k] pop ebx mov edi, [A + ebx*8 + edx*4] ; 取A[k][j] mov ebx, edi call mult_add ; 调用加法乘法子程序,eax = A[i][k] × A[k][j] add esi, eax ; 累加到临时寄存器 inc ebx jmp sum_loop_self end_sum_self: mov [C + ecx*8 + edx*4], esi ; 存入结果 inc edx jmp col_loop_self end_col_self: inc ecx jmp row_loop_self end_row_self: ; Linux系统调用退出程序 mov eax,1 xor ebx,ebx int 0x80
这种方法效率较低,仅适合必须禁用乘法/移位指令的特殊场景(比如某些受限嵌入式硬件)。
内容的提问来源于stack exchange,提问作者Sefi Caharon




