Laconic Deep Learning Inference Acceleration, ISCA 2019
◼︎ Abstract
곱 연산을 bit 수준에서 분해하는 하드웨어 가속기 Laconic을 통해 곱셈에 필요한 연산량을 40배 가까이 줄이는 결과를 얻음.
◼︎ Introduction
하드웨어 가속이 필요로 하는데 이 부분은 보통 MAC 연산과 이와 관련된 data 전송에서 bottleneck이 있음. 그래서 보통 4가지로 이를 해결함.
- 1. Data reuse: 데이터 흐름을 효율적으로 scheduling하는 방식
- 2. Data type and width: Quantization(16, 8 등등 비트수 낮추기)
- 3. Zero values(Sparsity): 0 값을 처리하는 방식(pruning)
- 4. Approximate computation: 정확하지 않고 근사치로
Our Focus
Bit Sparsity: 보통은 0값을 skip하려고 했다면 이 연구에서는 어떠한 값 내부의 비트에 0이 있는 경우의 연산을 skip하는 것을 목표로 함. 그래도 수학적으로는 같기 때문임.
Contrtibution
- 기존에 16bit 모델에서 0bit의 비율이 90% 이상이라는 것을 넘어 8bit, pruning 된 모델 등등 quantized 모델도 그러함을 증명
- Laconic 설계. 입력 데이터를 리스트로 encoding 하고 직렬로 곱하고 accumulate함. 병렬성 때문에 전체적인 latency 감소. 이를 LPE(Laconic Processing element)유닛으로 하여 면적이 크고 전력을 많이 먹는 곱셈기 대신 작은 adder를 많이 사용하여 조합한 방식으로 효율적인 설계.
◼︎ Bit Sparsity Is Abundant
Quantize, dense, prune 모델 등 bit level에서도 sparse함.
$o(n, y, x) = \sum_{k=0}^{C-1} \sum_{j=0}^{H_F-1} \sum_{i=0}^{W_F-1} w^n(k, j, i) \times a(k, j + y \times S, i + x \times S)$
- $a$: Activation channel로 $C \times H \times W$ (Channel, height, width)
- $S$: Stride
- $fmaps$: $C \times H_F \times W_F$ 가 K개
- $omap$: $K \times H_O \times W_O$
(논문에서 S=1만 나와있지만 달라져도 다 가능)
결론적으로는 이렇게 많은 부분에서 bit level sparsity가 발생함.
여기에서 A는 zero activation 즉 activation 값이 0일 때만 skip하는것, W는 Weight 값이 0일 때만 skip하는 것, At는 activation 값의 bit level에서 0을 skip하는 것, Wt는 weight 값이 bit level에서 0을 skip하는 것이다. 그래서 이 Laconic에서는 At+Wt 모두 적용해서 speed up이 어떤 모델에서든 크게 향상된 것을 보임.
우선 기본적인 bit level에서의 곱을 예시 값 활성화(A) = 15 (0000 1111)와 가중치(W) = 7 (0000 0111)의 곱셈 연산($15 \times 7 = 105$)으로 보면 다음과 같음
1. 일반적인 시프트 후 덧셈 방식 (Conventional Shift-and-Add)
가장 고전적인 직렬/순차적 곱셈 방식으로, 하위 비트부터 상위 비트까지 순회하며 비트가 1일 때마다 피승수(A)를 자릿수만큼 왼쪽으로 밀어(Shift) 가산기 트리로 누적함.
1
2
3
4
5
6
7
8
0 0 0 0 1 1 1 1 (A = 15)
× 0 0 0 0 0 1 1 1 (W = 7)
────────────────────────
0 0 0 0 1 1 1 1 <── W의 0번째 비트(1) × A = 15
0 0 0 1 1 1 1 0 <── W의 1번째 비트(1) × A = 30 (왼쪽으로 1칸 시프트)
+ 0 0 1 1 1 1 0 0 <── W의 2번째 비트(1) × A = 60 (왼쪽으로 2칸 시프트)
────────────────────────
0 1 1 0 1 0 0 1 <── 이진수 최종 합산 결과 = 105
2. 일반적인 비트 병렬 어레이 방식 (Conventional Bit-Parallel Array)
하드웨어에서는 자리수만 알아도 계산이 가능함. 우선 $A \times W = \sum_{i=0}^{7} \sum_{j=0}^{7} A_i \text{ AND } W_j$ 이런 형태로 계산이 됨.
1
2
3
4
5
6
7
8
9
10
11
12
13
W 비트열 (j) --> 0 0 0 0 0 1 1 1 (값: 7)
A 비트열 (i)
|
▼
7 (0) 0 0 0 0 0 0 0 0
6 (0) 0 0 0 0 0 0 0 0
5 (0) 0 0 0 0 0 0 0 0
4 (0) 0 0 0 0 0 0 0 0
3 (1) 0 0 0 0 0 1 1 1
2 (1) 0 0 0 0 0 1 1 1
1 (1) 0 0 0 0 0 1 1 1
0 (1) 0 0 0 0 0 1 1 1
(값: 15)
이렇게 되면 12개의 1값만 필요하게 됨. 왜냐하면
1
2
3
4
5
6
7
A 비트 위치 (i) ──> j=2 (4의자리) j=1 (2의자리) j=0 (1의자리)
│
▼
i=3 (8의 자리) 32 16 8
i=2 (4의 자리) 16 8 4
i=1 (2의 자리) 8 4 2
i=0 (1의 자리) 4 2 1
- $i=0$ 행 (1의 자리 비트가 만든 값들): $2^2 + 2^1 + 2^0 = 4 + 2 + 1 = \mathbf{7}$
- $i=1$ 행 (2의 자리 비트가 만든 값들): $2^3 + 2^2 + 2^1 = 8 + 4 + 2 = \mathbf{14}$
- $i=2$ 행 (4의 자리 비트가 만든 값들): $2^4 + 2^3 + 2^2 = 16 + 8 + 4 = \mathbf{28}$
- $i=3$ 행 (8의 자리 비트가 만든 값들): $2^5 + 2^4 + 2^3 = 32 + 16 + 8 = \mathbf{56}$
이제 하드웨어가 최종 출력 레이어에서 이 행들의 결과를 모두 합하면 \(\text{최종 연산 결과} = 7 + 14 + 28 + 56 = \mathbf{105}\). 즉 $2^{i+j}$만 있으면 연산을 할 수 있다. 그래도 어쨌든 $8 \times 8 = 64$번의 연산이 필요함. 0부분을 skip한다고 하더라도 이 경우에는 12번 즉, 1이 공통적으로 나오는 부분의 bit 수 만큼은 필요하다는 것임.
3. Laconic의 방식
Booth encoding이라는 방식을 통해 $\pm 2^x$형태로 $At_i$와 $Wt_j$가 생성돼서 $A \times W = \sum_{i=0}^{A_{terms}} \sum_{j=0}^{B_{terms}} At_i \times Wt_j$ 형태로 나오게 된다.
◼︎ LACONIC
Booth encoding
십진수를 계산할 때 99,999 × 7을 하려면 9를 다섯 번이나 곱해야 하지만 이를 (100,000 - 1) × 7로 바꾸어 700,000 - 7 = 699,993으로 훨씬 쉽게 계산하는 방식을 2진수에 적용하는 방법임. 2진수에서 1이 연속으로 등장하는 구간(String of 1s)이 있을 때, 이를 시작점($+$)과 끝점($-$)의 단 두 개의 거듭제곱 항으로 변환하면 됨.
$\sum_{k=n}^{m} 2^k = 2^{m+1} - 2^n$
60을 일반 2진수로 표현하면 0011 1100이고 $\text{Value} = 2^5 + 2^4 + 2^3 + 2^2 = 32 + 16 + 8 + 4 = 60$임.
Booth encoding을 사용하면 $2^6 - 2^2$로 변환함.즉, +2^6과 -2^2라는 단 2개의 부호가 있는 항(Terms)으로 $\text{Value} = 64 - 4 = 60$임.
부스 인코딩을 거친 거듭제곱 항($\pm 2^x$) 하나를 처리 장치로 보낼 때, 원본 8비트를 통째로 쓰지 않고 아래와 같은 4비트 압축 포맷으로 전선 신호를 인가함.
- MSB (가장 왼쪽 1비트): 부호(Sign) 비트 $\rightarrow$
0= 양수(+),1= 음수(-) - LSB (나머지 3비트): 지수(Exponent) 비트 $\rightarrow$ $0$부터 $7$까지의 자릿수를 2진수 3비트로 표현 (
000~111)
1) 10진수 15 (0000 1111) 부스 인코딩 격자 시각화
원본 8비트 데이터에서 연속된 1의 뭉텅이를 찾아내어 +2^4 와 -2^0 단 2개의 4비트 패킷 라인으로 직렬 변환하는 과정입니다. (파란색 칸 = 부호 비트 / 흰색 칸 = 지수 비트)
❶ 원본 8비트 데이터 (Raw Binary)
| 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
❷ 변환된 4비트 하드웨어 직렬 패킷 (Term 1 & Term 2)
- Cycle 0 :
+2^4항 (부호: + [0] / 지수: 4 [100])
| 0 | 1 | 0 | 0 |
- Cycle 1 :
-2^0항 (부호: - [1] / 지수: 0 [000])
| 1 | 0 | 0 | 0 |
2) 10진수 54 (0011 0110) 부스 인코딩 격자 시각화
내부 비트열에 1 뭉텅이가 두 군데로 쪼개져 있어, 부스 인코딩 시 +2^6, -2^4, +2^3, -2^1 총 4개의 패킷이 생성되어 4사이클 주기로 하드웨어 버스에 순차 인가됩니다.
❶ 원본 8비트 데이터 (Raw Binary)
| 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
❷ 변환된 4비트 하드웨어 직렬 패킷 (Term 1 ~ 4)
- Cycle 0 :
+2^6항 (부호: + [0] / 지수: 6 [110])
| 0 | 1 | 1 | 0 |
- Cycle 1 :
-2^4항 (부호: - [1] / 지수: 4 [100])
| 1 | 1 | 0 | 0 |
- Cycle 2 :
+2^3항 (부호: + [0] / 지수: 3 [011])
| 0 | 0 | 1 | 1 |
- Cycle 3 :
-2^1항 (부호: - [1] / 지수: 1 [010])
| 1 | 0 | 1 | 0 |
실제 계산
위에서 예시 그대로 가져와서 해보면
$A = 15$ (0000 1111) $\rightarrow$ $16 - 1 = \mathbf{(+2^4, -2^0)}$ [유효 항 2개]
$W = 7$ (0000 0111) $\rightarrow$ $8 - 1 = \mathbf{(+2^3, -2^0)}$ [유효 항 2개]
1
2
3
4
5
6
7
8
9
W의 유효 항 ──> +2^3 -2^0
A의 유효 항
│
▼
+2^4 [주기 1] [주기 2]
+2^7 -2^4
-2^0 [주기 3] [주기 4]
-2^3 +2^0
-
1주기: $+2^4 \times +2^3 \rightarrow \text{지수 합: } 4+3=7 \rightarrow +2^7 = \mathbf{128}$
-
2주기: $+2^4 \times -2^0 \rightarrow \text{지수 합: } 4+0=4 \rightarrow -2^4 = \mathbf{-16}$
-
3주기: $-2^0 \times +2^3 \rightarrow \text{지수 합: } 0+3=3 \rightarrow -2^3 = \mathbf{-8}$
-
4주기: $-2^0 \times -2^0 \rightarrow \text{지수 합: } 0+0=0 \rightarrow +2^0 = \mathbf{1}$
최종적으로 $128 - 16 - 8 + 1 = 105$
(a)는 그냥 기본적인 bit 형태의 곱임. 그래서 8bit로 들어오고 있음. 그래서 4cycle이 필요함. (b)는 LPE(Laconic PE) 한개가 어떻게 연산이 되고 있는지 보여줌. 이 경우는 cycle수가 2가 됐음. (c)는 이 LPE를 결국 격자형태로 많이 깔면 cycle 자체는 많이 늘어나보여도 그냥 bit parallel PE에 계속 넣는거보다 병렬성이 좋아서 가속됨.
최악의 경우(Worst-case), 8비트 값은 부스 인코딩되었을 때 최대 5개의 항(Term)으로 쪼개짐(10101010 or 01010101). 이는 모든 입력 조건에서 라코닉이 기존 방식보다 항상 최소한 같거나 빠르려면 비트 병렬 PE 1개당 25개의 LPE가 필요함을 의미함.
근데 이래도 더 빠를수 밖에 없는 이유는 worst case가 아닐 확률이 훨신 높음(Bit sparsity 높다는 얘기). 그리고 결국 곱셈기보다 훨신 작기 때문에 여러개 붙여도 곱셈기 여러개 생기는 것보다 훨씬 빠른 속도임.
Activation and Weight Representation
일단 memory에서 booth encoding 된 숫자 형태는 비효율적이기 때문에 memory에서 값을 저장할때는 보통의 2진수 상태로 둠. 그리고 weight와 activation을 16channel로 한번에 가져오는게 balance 있다고 하는데 아마 이건 8bit에 16channel이라서 $8 \times 16 = 128$이라서 SRAM 크기에도 좋고 CNN 구조에서 filter를 $4 \times 4$로 많이 했다 보니 이때 filter의 channel은 정확히 16개이긴 함.
\[\begin{aligned} W \times A &= \sum_{\forall(s,t) \in W_{terms}} (-1)^s 2^t \times \sum_{\forall(s',t') \in A_{terms}} (-1)^{s'} 2^{t'} \\ &= ((-1)^{(s_0+s'_0)}2^{(t_0+t'_0)} + \dots + (-1)^{(s_0+s'_m)}2^{(t_0+t'_m)}) \\ &\quad + \dots + ((-1)^{(s_n+s'_0)}2^{(t_n+t'_0)} + \dots + (-1)^{(s_n+s'_m)}2^{(t_n+t'_m)}) \end{aligned}\]$s$는 weight 값의 sign bit $t$는 weight 값의 지수값 $s’$은 activation 값의 sign bit $t’$은 activation 값의 지수값임. 이걸 하드웨어적으로 만들면 figure 3와 같음.
A Histogram-Based PE
여기서는 16 channel일때 흐름을 봄. 크게 6단계로 나눠짐.
Step 1 : 지수 덧셈 및 부호 판별
16개의 3비트 weight 지수 항(\((t_0 \dots t_{15})\))과 sign bit(\((s_0 \dots s_{15})\)), 그리고 16개의 3비트 activation 지수 항(\((t'_0 \dots t'_{15})\))과 sign bit(\((s'_0 \dots s'_{15})\))를 입력받아 곱을 계산함. 지수들을 서로 더하여 4비트 크기(7+7 = 14)의 지수 결과물(\((t_0+t'_0) \dots (t_{15}+t'_{15})\))을 생성하고, sign bit 끼리는 XOR 게이트로 \(E_{0\text{sign}} \dots E_{15\text{sign}}\).
Step 2 : 디코더를 통한 원핫(One-hot) 변환
$i$번째 액티베이션 및 가중치 쌍(\(i \in \{0 \dots 15\}\))에 대하여, LPE는 4b-to-16b 디코더를 통해 \(2^{t_i+t'_i}\)를 계산함. 4비트 지수 합을 하나의 ‘1’ 비트와 15개의 ‘0’ 비트로 이루어진 16비트 one-hot format으로 변환(해당 자리수에 1이 active되는 형태)하며, 이 ‘1’의 위치는 부호 결과(\(E_i.\text{sign}\))에 따라 $+2^j$ 또는 $-2^j$ 값을 의미하게 됨.
Step 3 : 히스토그램 버킷 누적
Step 2에서 나온 16개의 16비트 숫자들을 16개의 버킷($N^0 \dots N^{15}$)에 누적하여 디코더 출력값들의 히스토그램을 생성함. 16개의 버킷은 $2^0 \dots 2^{15}$ 자릿수에 대응합니다. 버킷은 최대 16개의 부호가 있는 입력을 받으므로 최종 카운트 범위는 $[-16 \dots 16]$이 되며, 2의 보수 형태의 6비트로 표현됨.
Step 4 : 비트 결합을 통한 가산기 제거
원래는 16개의 6비트 카운트 값들을 shift를 거쳐 16입력 가산기 트리를 써야 하지만 Laconic은 비트 자리가 절대 겹치지 않는 버킷 구조를 활용하여 전선을 그대로 이어 붙이는 Concatenation 을 사용함.
Step 5 : 압축된 가산기 트리 연산
Step 4에서 전선 결합으로 1차 압축된 값들은 최종 6입력 가산기 트리를 통해 더해져 최종 22비트 크기의 partial sum을 도출함.
Step 6 : 최종 psum 누적
이전 단계에서 도출된 부분합이 psum accumulator에 최종 누적됨.
4 Channel Example
■ Setting
| 입력 채널 | ti | t'i | si | s'i | Value |
|---|---|---|---|---|---|
| Ch 0 | 1 (3b'001) | 1 (3b'001) | 0 (+) | 0 (+) | +2¹ × 2¹ = +4 |
| Ch 1 | 2 (3b'010) | 0 (3b'000) | 1 (-) | 0 (+) | -2² × 2⁰ = -4 |
| Ch 2 | 2 (3b'010) | 1 (3b'001) | 0 (+) | 0 (+) | +2² × 2¹ = +8 |
| Ch 3 | 0 (3b'000) | 1 (3b'001) | 0 (+) | 0 (+) | +2⁰ × 2¹ = +2 |
최종적으로 +4-4+8+2 = 10이 나와야 함.
■ Step 1
■ Step 2
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| Ch 0 (E=2): | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| Ch 1 (E=2): | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| Ch 2 (E=3): | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| Ch 3 (E=1): | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
■ Step 3
| 담당 버킷 | Ch 0 [MSB,LSB] |
Ch 1 [MSB,LSB] |
Ch 2 [MSB,LSB] |
Ch 3 [MSB,LSB] |
Caculation | Output (Nj) |
|---|---|---|---|---|---|---|
| 버킷 N¹ (2¹) | [0][0] | [0][0] | [0][0] | [0][1] | 1 - 0 = 1 | [0][0][0][0][0][1] (+1) |
| 버킷 N² (2²) | [0][1] | [1][1] | [0][0] | [0][0] |
1 - 1 = 0 |
[0][0][0][0][0][0] (0) |
| 버킷 N³ (2³) | [0][0] | [0][0] | [0][1] | [0][0] | 1 - 0 = 1 | [0][0][0][0][0][1] (+1) |
총 6bit로 맨 왼쪽(MSB) 1bit가 sign bit 이고 1111+1111 = 10000를 나타내야하니까 하위 5bit가 필요함
■ Step 4
원래는 shift 해야하지만 wire로 분리돼있어서 하드웨어적으로는 그냥 사용하면 됨
■ Step 5
$$\text{psum} = (1 \times 2) + (0 \times 4) + (1 \times 8) = 2 + 0 + 8 = 10$$
■ Step 6
psum = 10과 다음 cycle에 나올 다른 psum들을 합침
Shrinking the Adder Tree
원래는 step 4에서 shift 해서 자리수 맞춰주고 16개의 값을 다 더하면 16 to 1 adder tree를 써서 굉장히 비효율적임. 근데 이걸 더 작은 6 to 1 adder tree로 줄여서 사용할 수 있다. 우선 \(N^6\)의 경우 \(<<6\)을 하게 됨. 이걸 수식으로 나타내면 아래처럼 씀.
\[\begin{aligned} N^6 \times 2^6 + N^0 =&\ \\ \text{1) if } n_5^0 = 0: =&\ n_5^6 n_4^6 n_3^6 n_2^6 n_1^6 n_0^6 000000 + 000000 n_5^0 n_4^0 n_3^0 n_2^0 n_1^0 n_0^0 \\ =&\ n_5^6 n_4^6 n_3^6 n_2^6 n_1^6 n_0^6 n_5^0 n_4^0 n_3^0 n_2^0 n_1^0 n_0^0 = \{N^6, N^0\} \\ \text{2) if } n_5^0 = 1: =&\ n_5^6 n_4^6 n_3^6 n_2^6 n_1^6 n_0^6 000000 + 111111 n_5^0 n_4^0 n_3^0 n_2^0 n_1^0 n_0^0 \\ =&\ (n_5^6+1)(n_4^6+1)(n_3^6+1)(n_2^6+1) \cdots \\ &\ \cdots (n_1^6+1)(n_0^6+1)n_5^0 n_4^0 n_3^0 n_2^0 n_1^0 n_0^0 = \{(N^6-1), N^0\} \end{aligned}\]이렇게 두면 이해하기 어려우니 실제 수식으로 보여주면
\(N^6 = 5\)(000101)일 때,
-
Case 1: \(N^0 = 3\) (000011), \(5 \times 2^6 + 3 = 320 + 3 = 323\)
-
Case 2: \(N^0 = -3\) (111101), \(5 \times 2^6 + (-3) = 320 - 3 = 317\)
Case 1
| Bit Position | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| N⁶ ≪ 6 (5 ≪ 6) | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| + (CONCAT) | ||||||||||||
| N⁰ (3) | 0 | 0 | 0 | 0 | 1 | 1 | ||||||
| 결과 (323) | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
Case 2
| Bit Position | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| N⁶ ≪ 6 (5 ≪ 6) | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| + (ADD) | ||||||||||||
| N⁰ 부호 확장 (-3) | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
| 산술 결과 (317) | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 1 |
case 2 일때 보면 12자리수에 어짜피 더해진 1값은 버려져서 결국 값은 317이 나오게 됨. 즉 서로 6씩 shift 된 \(N\)값들은 서로 영향을 안끼침. 그래서 \(N^6\)은 \(N^12\)와도 같은 관계이므로 이런 관계의 \(N\)값 끼리 묶어주면
\[\begin{aligned} G_0 = \{N^{12}, N^6, N^0\}, \quad G_1 = \{N^{13}, N^7, N^1\}, \quad G_2 = \{N^{14}, N^8, N^2\}, \\ G_3 = \{N^{15}, N^9, N^3\}, \quad G_4 = \{N^{10}, N^4\}, \quad G_5 = \{N^{11}, N^5\} \end{aligned}\]으로 표현 가능하고
\[psum = \sum_{i=0}^{5} (G_i \ll i)\]최종적으로 이렇게 6개의 18bit의 값만 합산 하면 됨. 이걸 보여주는 것이 figure 3(b)임.
| Bit Position | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| G₀ ≪ 0 (18-bit) | sign ext. | N¹² | N⁶ | N⁰ | ||||||||||||||||||
| G₁ ≪ 1 (18-bit) | sign ext. | N¹³ | N⁷ | N¹ | 0 | |||||||||||||||||
| G₂ ≪ 2 (18-bit) | sign ext. | N¹⁴ | N⁸ | N² | 0 | |||||||||||||||||
| G₃ ≪ 3 (18-bit) | s | N¹⁵ | N⁹ | N³ | 0 | |||||||||||||||||
| G₄ ≪ 4 (12-bit) | sign ext. | N¹⁰ | N⁴ | 0 | ||||||||||||||||||
| G₅ ≪ 5 (12-bit) | sign ext. | N¹¹ | N⁵ | 0 | ||||||||||||||||||
| 6-Input Adder Tree | 최종 22-bit 부분합 출력 수 정렬 결과 (psum) | |||||||||||||||||||||
A Lower Area Histogram
Figure 3에서 (2)one-hot과 (3)histogram 사이에 \(D_0^0, D_0^1, D_0^2, \dots, D_0^{15}\)이 16개 그대로 들어가지만 이것보다 \((D_0^0, D_0^1), (D_0^2, D_0^3) \dots ,(D_0^{14}, D_0^{15})\) 이렇게 둘씩 짝지어서 8개 씩 들어감.
Tile Organization
지금까지 하나의 LPE에 대해서 했지만 tile처럼 여러개의 LPE를 깔면 병렬적으로 한번에 처리 가능하고 어짜피 연산에는 수십 cycle이 걸리기 때문에 data의 병목도 생기지 않음.
Tile Synchronization
또 이렇게 tile로 두면 문제는 term의 개수가 다르다는 것임. 이렇게 되면 어떤건 빠르게 끝나고 어떤건 느리게 끝남. Barrier 방식을 사용하면 제일 느린 LPE가 연산을 끝날때 까지 stall 했다가 다음 것을 하면 되는데 이러면 buble이 심해짐. 그래서 Com Synchronization이라는 대안을 내고 앞에 queue buffer를 달아줘서 해결함.
Data Type Composability
8b 말고도 16b를 써야할 수 도 있는데 그럴때는 figure 4의 (c)처럼 확장하면 됨. Composer 열을 추가하면 됨. 그리고 9b나 10b처럼 미세한 정밀도를 요구할 때는 booth encoder의 scheduling을 이용하면 가능함.
◼︎ Evaluation
전체적으로 빨라지고 에너지 효율도 좋아졌지만 Inception V3 에서는 에너지 효율에서는 오히려 손해가 있었고 1b/2b 급의 압축된 모델에서는 오히려 성능과 효율 모두 손해였음. 즉, bit 크기가 어느정도는 있어야 하고 에너지 효율이 살짝씩 떨어질 가능성은 존재함.