You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

汇编语言中矩阵乘法实现:含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

火山引擎 最新活动