모든 이야기

LMUL: 원소를 더 담으면 임시 레지스터가 줄어드는 이유

벡터 원소를 더 많이 처리하려고 LMUL을 키웠는데, 사용할 수 있는 임시 레지스터가 급격히 줄었다. 이후 widening 연산까지 넣으니 아예 허용되지 않는 레지스터 그룹이 생겼다. LMUL은 계산기 개수를 늘리는 버튼이 아니라, 벡터 레지스터 이름을 묶는 설정이기 때문이다.

한 명령의 최대 원소 수를 계산한다

RISC-V 벡터에서 VLEN은 레지스터 한 개의 비트 길이, SEW는 설정한 원소 폭이다. 정수 LMUL=2,4,8은 각각 그 수만큼의 레지스터를 한 그룹으로 묶는다. 이때 최대 원소 수는 다음과 같다.

VLMAX = LMUL × VLEN / SEW

설명용 구현의 VLEN을 256비트로 놓으면 다음 표를 얻는다. 각 설정과 연산의 지원을 전제로 한다.

SEWLMUL그룹의 비트 수VLMAX
3212568
32251216
324102432
6425128

SEW만 두 배로 바꾸면 같은 공간에 들어가는 원소는 절반이다. 반대로 LMUL을 함께 두 배로 키우면 원소 수를 유지할 수 있다. 하지만 물리 레지스터 한 개의 VLEN이 바뀐 것은 아니다. 여러 레지스터를 한 피연산자로 읽게 된 것이다. RISC-V Vector, Vector Register Grouping

32개의 벡터 레지스터를 겹치지 않는 전체 그룹으로 나눌 때 LMUL=1에서는 32그룹, 2에서는 16그룹, 4에서는 8그룹, 8에서는 4그룹이다. 이는 mask나 다른 연산의 제약을 넣기 전의 이름 배분이다. 한 그룹이 커질수록 여러 입력과 임시 결과를 동시에 보관할 공간은 더 빨리 줄어든다.

widening의 목적지는 두 배의 그룹을 요구한다

SEW=32, LMUL=4에서 32비트 입력을 더해 64비트 결과를 만드는 widening add를 생각해 보자. 입력 그룹은 4개 레지스터에 최대 32원소를 담는다. 같은 32원소를 64비트로 저장하려면 결과에 2048비트, 즉 8개 레지스터가 필요하다.

원문은 각 피연산자의 실제 그룹 배율을 EMUL로 구분한다. 이 widening에서 입력 EMUL은 4, 목적지 EMUL은 8이다. 예를 들어 입력을 v8v11과 v12v15에 두고 결과를 v16~v23에 쓰면 세 그룹이 겹치지 않는다. v8과 v12는 4개 그룹의 시작 조건을, v16은 8개 그룹의 시작 조건을 만족한다. RISC-V Vector, Vector Operands

목적지를 v12로 옮기는 것은 단순히 겹침만 따질 문제가 아니다. 8개 그룹은 8의 배수 번호에서 시작해야 하므로, v12를 8개 그룹의 시작으로 쓰는 encoding은 reserved다. 소프트웨어가 “지금 vl이 작으니 실제로 두 레지스터만 쓰겠다”고 주장해도 이 그룹 지정 조건이 사라지지 않는다.

같은 widening을 LMUL=8에서 하면 목적지 EMUL은 16이 된다. 규약은 한 명령에서 사용하는 그룹이 8개 레지스터를 넘는 encoding을 reserved로 둔다. vl=1로 설정해 실제 계산 원소를 하나로 줄여도 이 명령의 그룹 요구를 허용되는 것으로 바꾸지는 못한다.

모든 겹침을 금지하면 규약보다 좁아진다

반대로 “목적지와 입력은 언제나 분리해야 한다”는 규칙도 정확하지 않다. 원문에는 폭과 그룹 위치에 따른 겹침 허용 조건이 있다. 목적지가 더 넓은 경우에는 입력 EMUL이 1 이상이고, 겹친 입력 그룹과 목적지 그룹의 가장 높은 레지스터 번호가 같아야 한다.

위 e32,m4 widening의 목적지를 v8v15로 두고, 한 입력을 v12v15로 두는 경우는 이 조건에 맞는다. 둘의 가장 높은 번호가 v15이기 때문이다. 다른 입력을 v16v19에 두면 그 입력은 겹치지 않는다. 반면 목적지 v8v15와 입력 v8~v11을 겹치게 하면 가장 높은 번호가 달라 이 조건을 만족하지 못한다. 더 좁은 목적지와 같은 폭의 경우에는 다른 허용 규정이 적용된다. RISC-V Vector, register group overlap constraints

이 조건은 구현의 복구 가능한 예외 처리와도 연결된다. 겹침 여부만 보거나 시작 번호만 맞추는 검사는 부족하며, 각 피연산자의 폭과 EMUL을 함께 읽어야 한다.

fractional LMUL은 레지스터 이름을 늘리지 않는다

LMUL=1/2처럼 작은 배율도 있다. VLEN=256, ELEN=64인 지원 구현에서 SEW=16, LMUL=1/2를 쓰면 그룹은 레지스터 하나의 절반인 128비트에 최대 8원소를 담는다. 서로 다른 짧은 값을 32개의 이름에 따로 두면서, 더 넓은 중간 결과가 여러 레지스터를 요구하는 부담을 줄일 수 있다.

그렇다고 나머지 절반에 새 이름이 생겨 64개 레지스터를 할당하는 것은 아니다. 여전히 이름은 v0~v31이며, 사용하지 않는 부분이 남는다. 지원 가능한 원소 폭도 fractional LMUL과 ELEN 등의 조건에 묶이므로 원하는 조합을 모두 쓸 수 있다고 가정해서는 안 된다.

LMUL을 고를 때의 질문은 “몇 배 빠른가”보다 “이 원소 폭과 연산에서 각 그룹이 몇 개의 이름을 차지하는가”에 가깝다. 큰 LMUL은 한 명령의 최대 작업량을 늘릴 수 있지만 lane 수나 실제 vl을 보장하지 않는다. 입력과 widening 결과의 EMUL, 그룹 정렬과 겹침, 남겨 둘 임시 공간까지 계산해야 늘린 용량을 올바른 코드로 사용할 수 있다.

LMUL: 원소를 더 담으면 임시 레지스터가 줄어드는 이유 · ariser