MLA, MLS
MLA
Multiply-Accumulate — NEON 벡터
정의: 두 벡터의 각 레인(lane)을 곱한 뒤, 그 결과를 목적 벡터에 누적(더함) 하는 NEON 전용 명령어. 이전에 다룬 스칼라 계열 MADD(Multiply-Add)와 개념은 완전히 동일하나, 벡터의 모든 레인에 동시에 적용된다는 점이 핵심 차이임.
문법
MLA Vd.<T>, Vn.<T>, Vm.<T> // 벡터-벡터 레인별 곱셈-누적
MLA Vd.<T>, Vn.<T>, Vm.<Ts>[index] // 벡터-스칼라(특정 레인) 곱셈-누적
<T>: 데이터 배열 타입 (예:8B,4H,2S등)Vd: 결과가 누적될 목적 벡터 (기존 값 위에 더해짐)Vn,Vm: 곱셈할 두 소스 벡터
동작 수식 (레인별로 동시 수행):
Vd[i] = Vd[i] + (Vn[i] × Vm[i]) for all lanes i
이전 MADD 문서와의 직접 비교
스칼라 MADD (복습):
MADD Xd, Xn, Xm, Xa // Xd = Xa + (Xn × Xm)
벡터 MLA:
MLA Vd.4S, Vn.4S, Vm.4S // Vd = Vd + (Vn × Vm) ← 4개 레인 동시에!
핵심 차이점:
| 구분 | MADD (스칼라) | MLA (벡터) |
|---|---|---|
| 오퍼랜드 수 | 4개 (Xd, Xn, Xm, Xa 별도) | 3개 (Vd가 누산기 겸 결과) |
| 누적 대상 | 별도 레지스터(Xa) 지정 가능 | Vd 자기 자신에 강제로 누적 |
| 처리 단위 | 스칼라 값 하나 | 벡터의 모든 레인 동시 (SIMD) |
| 한 번에 처리량 | 1개 곱셈-덧셈 | 최대 16개(8비트 기준) 동시 |
중요한 제약: MADD와 달리, MLA는 누적 대상이 반드시 목적 레지스터 자신(Vd) 이어야 함. “어디에 누적할지”를 별도로 지정할 수 없고, 항상 Vd += Vn × Vm 형태로 고정됨.
예제 코드 — 기본 벡터 곱셈-누적
.text
_start:
// v0 = 초기 누적값, v1 = 곱할 값 A, v2 = 곱할 값 B (각각 4개의 32비트 레인)
ld1 {v0.4s}, [x0]
ld1 {v1.4s}, [x1]
ld1 {v2.4s}, [x2]
mla v0.4s, v1.4s, v2.4s // v0[i] += v1[i] × v2[i], i=0~3 동시에
st1 {v0.4s}, [x3] // 결과 저장
직관적 이해: 이 한 줄로 실제로는 다음과 같은 4개의 스칼라 연산이 동시에 수행됨:
v0[0] += v1[0] × v2[0]
v0[1] += v1[1] × v2[1]
v0[2] += v1[2] × v2[2]
v0[3] += v1[3] × v2[3]
MLA의 대표적 실전 용도 — 내적(Dot Product)과 컨볼루션
1. 벡터 내적(Dot Product) 계산:
// C 코드
float dot_product(float a[4], float b[4]) {
float sum = 0;
for (int i = 0; i < 4; i++)
sum += a[i] * b[i];
return sum;
}
// 순수 MLA로 4개 곱셈-누적을 한 번에 처리 (레인별 누적 후, 최종 수평 합산 필요)
ld1 {v0.4s}, [x0] // a[]
ld1 {v1.4s}, [x1] // b[]
movi v2.4s, #0 // 결과 초기화(0)
mla v2.4s, v0.4s, v1.4s // v2[i] = a[i]*b[i], i=0~3 (누적 없이 이번엔 첫 곱셈이므로 결과=곱셈값)
// 레인 4개를 하나의 스칼라 합으로 모으는 후처리(수평 합산) 필요
faddp v3.4s, v2.4s, v2.4s
faddp s0, v3.2s
참고: 부동소수점의 경우 정수용 MLA가 아니라 FMLA(Floating-point Multiply-Add)를 사용함 — 아래 별도 설명.
2. 신호 처리 / 컨볼루션(Convolution) 필터:
// 간단한 FIR 필터 개념
for (i = 0; i < N; i++)
output[i] += input[i] * coefficient[i];
이런 반복적 곱셈-누적 패턴은 오디오 필터, 이미지 컨볼루션(블러, 샤프닝 등)의 핵심 연산이며, MLA가 SIMD 폭만큼(예: 16개 8비트, 8개 16비트, 4개 32비트) 한 번에 처리하므로 스칼라 루프 대비 수 배의 성능 향상을 가져옴.
MLS
Multiply-Subtract — MLA의 형제 명령어
정의: MLA의 뺄셈 버전. 이전 스칼라 문서의 MSUB에 대응됨.
MLS Vd.<T>, Vn.<T>, Vm.<T>
동작:
Vd[i] = Vd[i] - (Vn[i] × Vm[i])
mls v0.4s, v1.4s, v2.4s // v0[i] -= v1[i] × v2[i]
용도: 오차(residual) 계산, 예측값 차감 등 곱셈 결과를 빼야 하는 신호처리 알고리즘에서 활용됨.
FMLA / FMLS — 부동소수점 버전
중요한 구분: 정수 벡터에는 MLA/MLS, 부동소수점 벡터에는 별도로 FMLA/FMLS가 존재함.
FMLA Vd.4S, Vn.4S, Vm.4S // 부동소수점 곱셈-누적: Vd += Vn × Vm
FMLS Vd.4S, Vn.4S, Vm.4S // 부동소수점 곱셈-감산: Vd -= Vn × Vm
정수 vs 부동소수점 대응표:
| 데이터 타입 | 곱셈-누적 | 곱셈-감산 |
|---|---|---|
| 정수(Integer) | MLA |
MLS |
| 부동소수점(Float) | FMLA |
FMLS |
실전 활용 빈도: 그래픽스, 머신러닝(행렬 곱셈), 오디오 처리 등 대부분의 실전 수치 연산은 부동소수점 기반이므로, 실제 macOS/iOS 앱(특히 Metal, Core ML, Accelerate framework 기반 코드)을 디스어셈블하면 순수 정수 MLA보다 FMLA가 훨씬 자주 관찰됨.
Vector-Scalar 형태 — 특정 레인만 곱할 때
용도: 벡터 전체가 아니라, 다른 벡터의 특정 레인 하나만 스칼라처럼 뽑아서 곱셈-누적할 때 사용됨.
MLA Vd.4S, Vn.4S, Vm.S[index]
예시:
mla v0.4s, v1.4s, v2.s[1] // v2의 1번 레인 값을 모든 v1 레인에 곱해 v0에 누적
실전 활용: 행렬-벡터 곱셈(matrix-vector multiplication)에서 행렬의 한 열(column) 값을 모든 행에 동시에 곱해 누적하는 패턴에서 자주 사용됨.
종합 정리표
| 명령어 | 데이터 타입 | 연산 | 이전 스칼라 대응 |
|---|---|---|---|
MLA |
정수 벡터 | Vd += Vn×Vm |
MADD의 벡터 버전 |
MLS |
정수 벡터 | Vd -= Vn×Vm |
MSUB의 벡터 버전 |
FMLA |
부동소수점 벡터 | Vd += Vn×Vm |
(스칼라 부동소수점 FMADD 존재) |
FMLS |
부동소수점 벡터 | Vd -= Vn×Vm |
(스칼라 부동소수점 FMSUB 존재) |
최종 결론
MLA는 이전에 다룬 MADD(스칼라)와 정확히 같은 발상 — “곱셈과 덧셈을 하나의 명령어로 묶어 효율을 높인다” — 을 벡터(SIMD) 레벨로 확장한 명령어임. 특히 내적(dot product), 행렬 곱셈, 디지털 필터(컨볼루션) 같은 “곱하고 누적하는” 패턴이 반복되는 모든 수치 연산 알고리즘의 핵심 빌드블록이며, 실전에서는 정수용 MLA보다 그래픽스·머신러닝 맥락에서 압도적으로 많이 쓰이는 부동소수점 버전 FMLA를 훨씬 자주 마주치게 될 것임.