인텔 10nm 프로세스의 코어 프로세서 CPU인 서니 코브(Sunny Cove)는 마이크로 아키텍처가 비교적 크게 바뀌었습니다. 현재 내용이 알려진 건 실행 파이프/메모리 뿐이나 그것만으로도 큰 변화가 있었음을 알 수 있습니다. 서니 코브를 기존의 스카이레이크와 비교하면 이런 식입니다.
서니 코브의 블럭 다이어그램 추측
스카이레이크의 블럭 다이어그램
10nm 세대의 퍼포먼스 CPU 코어, 서니 코브
스카이레이크의 AVX-512는 서버용으로 개발된 스카이레이크-X에서만 지원합니다. 그러나 서니 코브는 AVX-512를 기본적으로 지원한다고 보입니다. AVX-512는 기존 AVX의 SIMD (Single Instruction, Multiple Data)를 2배로 늘린 게 전부가 아닙니다. 벡터 유닛에선 획기적인 변화가 있어 어찌보면 GPU와 비슷합니다.
AVX-512은 원래 GPU에 맞서 개발된 라라비 명령 확장(Larrabee New Instructions) 에서 발전한 명령어입니다. 라라비에서 제온 파이를 거치면서 명령 포맷은 바뀌었으나 기본적인 특징은 라라비의 새 명령 확장을 계승합니다. 512비트의 넓은 SIMD 유닛에 2비트 단정밀도 부동소수점 연산은 16wide로 수행합니다. 또 프레디케이션을 본격적으로 지원하는 등, 새로운 요소가 AVX에 추가됐습니다. 서니 코브의 AVX-512는 이런 특징을 잘 살렸습니다.
서니 코브의 AVX-512 구현은 스카이레이크-X의 구현을 확장한 것으로 추측됩니다. 현재 스ㅏ이레이크는 포트 0과 포트 1에 각각 256비트 SIMD FMA 연산 유닛이 있고, 포트 5에 256비트 셔플 유닛이 있습니다. 스카이레이크-X에선 이를 확장해 기존의 포트 0과 포트 1의 256비트 SIMD FMA 유닛 2개를 묶어 512비트 SIMD 유닛으로 씁니다. 또 포트 5에 새로 512비트 FMA 유닛을 배치합니다. 3개의 포트를 써서 512비트 FMA를 2개 병렬 수행합니다.
512비트의 넓은 레지스터 액세스
그럼 왜 포트 0과 포트 1은 256비트 FMA 디바이스일까요. 기존의 256비트 AVX에서 포트 5 FMA를 끄고 전력 사용량을 절감하기 위한 방법으로 보입니다. 또 레지스터 액세스 포트의 효율도 높일 필요가 있습니다. 포트 0, 1, 5는 제각각 SIMD 레지스터 액세스 포트가 있는데 이것의 설계는 매우 성가십니다.
인텔은 AVX-512에서 새로 512비트의 레지스터인 ZMM을 도입했습니다. 논리 레지스터는 32개, 서니 코브의 물리 레지스터 수는 아직 알려지지 않았습니다. 이렇게 긴 벡터를 구현하려면 레지스터 액세스가 문제가 됩니다. FMA 연산에선 1사이클의 레지스터에 3 읽기와 1 쓰기가 필요합니다. AVX-512에선 너비가 512비트며 유닛이 2개입니다. 512비트 읽기 경로가 6개, 쓰기 경로가 2개니 이것의 배선만 따져도 복잡합니다.
이 문제를 해결하는 방법 중 하나가 512비트 레지스터 파일을 256비트식 분할 구현하는 겁니다. 512비트를 32비트/64비트로 레인을 나누니 분할 액세스가 가능합니다. 256비트는 512비트의 절반 크기만 있으면 됩니다. 서니 코브는 포트 0과 포트 1의 256비트 벡터 유닛이 각각 512비ㅡ 레지스터를 256비트 분할해 액세스합니다. 포트 0이 어퍼, 포트 1이 로워 같은 식이 됩니다.
포트 0과 포트 1은 각각 256비트 YMM 레지스터 액세스 포트를 갖고 있습니다. 따라서 포트 0과 포트 1을 결합해 512비트 AVX-512 연산의 경로로 쓰면 포트 0과 1 중 하나는 기존의 256비트 레지스터 액세스 포트를 그대로 쓸 수 있습니다. ZMM 레지스터와 YMM 레지스터의 확장으로 실제 구현은 256비트 이상이 됩니다.
ZMM 레지스터의 액세스가 필요한 셔플 유닛
포트 0과 1의 조합츠럼 FMA 연산 같은 일반적인 연산 유닛은 512비트 레지스터 파일을 256비트씩 분할해서 쓸 수 있습니다. 그러나 같은 AVX 유닛 중에서도 셔플 유닛은 그렇지 않습니다. 셔플 유닛은 벡터 레지스터의 슬롯 내용을 대체하기 위해 ZMM 레지스터의 모든 슬롯에 액세스해야만 합니다. 그래서 512비트 레지스터 전체에 연결되야 합니다.
따라서 셔플 유닛은 포트 5에 배치하는 게 일반적입니다. 포트 5의 벡터 연산 유닛은 512비트로, 512비트의 ZMM 레지스터 전체에 액세스 포트를 갖고 있습니다. 이것이 스카이레이크-X의 셔플 유닛 구현 방식입니다. 그런데 서니 코브는 셔플 유닛을 포트 1에도 넣었습니다. 이 두번째 셔플 유닛에 대해선 자세히 알려지지 않았습니다.
이게 256비트의 YMM 전용 셔플 유닛이라면 원래의 포트 1 YMM 레지스터 액세스 패스를 씁니다. 하지만 두번째의 512비트 셔플 유닛이라면 512비트 ZMM 레지스터 패스가 필요합니다. 하지만 포트 0과 1 중 하나는 256비트로 나눈 ZMM 레니즈스터의 로워/어퍼 모두에 액세스가 가능할 겁니다. 그래서 여기에 셔플 유닛을 배치해도 문제가 없습니다. 그렇다면 1 사이클에 최대 2개의 AVX-512 셔플 명령을 실행할 수 있습니다.
그럼 셔플 유닛이 왜 2개나 필요할까요? AVX-512의 벡터가 길고, 효율적인 벡터 프로세싱을 실현하기 위해서 Structure of Arrays (SOA)의 배열 처리가 중요해서입니다. AOA를 위한 작업 항목의 메모리 레이아웃 변환을 위해 셔플 유닛이 중요하다고 추측됩니다.
SOA 레이아웃 효율화의 AVX-512
이것이 AVX-512의 가장 중요한 부분입니다. SOA 레이아웃 실행은 AVX-512의 핵심이며, AVX-512가 기존의 SSE/AVX와 본질적으로 다른 부분입니다. SOA를 효율적으로 수행할 수 있도록 AVX-512에서 패널티 없이 프레디케이션 기능도 구현했습니다. 이는 GPU처럼 플로우 컨트롤이 가능한 벡터 유닛이 됐음을 의미합니다.
벡터 프로세싱의 메모리 에이아웃은 크게 두 가지가 있습니다. 하나는 Array of Structures (AOS) 혹은 Packed나 SIMD라 부르는 방식입니다. 다른 하나는 Structure of Arrays (SOA) 혹은 스칼라, SIMT(Single Instruction, Multiple Thread)라고 부르는 방식입니다 AOS/팩드/SIMD는 데이커를 일정 크기로 압축해 처리합니다. 반면 SOA/스칼라/SIMT는 패키지를 분해해 구성 요소를 같은 형태로 묶어 처리합니다.
AOS는 팩 처리의 크기가 정해져 있어 효율이 나빠질 수도 있습니다. 3개의 구성 요소를 4웨이 벡터에서 실행한다면 나머지 한 개의 슬롯이 낭비됩니다. 반면 SOA는 패키지를 나눠 배열에 채우기에 낭비되는 부분이 없습니다. 4웨이 백터라면 3개로 나눈 팩을 4개 가져와 3 사이클로 실행합니다. 벡터 길이가 커질수록 이 문제는 복잡해집니다.
그래서 벡터기 갈면 SOA를 씁니다. SOA를 효율적으로 처리하기 위해 벡터의 각 레인에 플로우 컨트롤 기능을 넣는 게 일반적입니다. 512비트라면 단정밀도 16레인이지만, 16레인이 모두 같은 명령을 실행한다면 이를 지원하는 애플리케이션에만 한정됩니다. 하지만 16레인의 명령 실행 여부를 따로 설정할 수 있다면 애플리케이션에 적용하기가 쉬워집니다.
마스크 레지스터 프로그램을 사용한 프레디케이션이 지금의 대세
이런 벡터 플로어 컨트롤엔 마스커 레지스터를 사용한 프레디케이션의 수행이 일반적입니다. 프레디케이션에선 각 레인마다 명령을 실행할지를, 혹은 연산 결과를 레지스터에 기록할지를 마스크 레지스터를 써서 선택합니다. 전용 마스크 레지스터를 사용해 프레디케이션을 차별 없이 제공합니다. 프레디케이션을 사용하면 벡터의 각 레인에 조건 분기가 생겨도 원래 분기인 것처럼 보이게 만듭니다.
벡터 길이를 넓혀 마스크 레지스터로 프레디케이션하고, SOA를 지원하는 구조는 현재 CPU 벡터 유닛의 대세입니다. arm의 Scalable Vector Extention (SVE)와 RISC-V의 벡터 ISA는 어느정도 비슷한 마스크 레지스터로 프레디케이션을 지원합니다. NVIDIA 나 AMD, Imagination Technologies 등의 GPU도 마찬가지입니다. AVX-512도 비슷합니다. 정확하게 이 기능은 AVX-512의 바탕이었던 라라비 NI(Larrabee New Instructions)에서 구현됐기에 CPU에선 인텔이 앞섰다고 할 수 있습니다. 이걸 인텔 CPU 전체로 보급하는 과정입니다.
AOS와 SOA의 변환에 필요한 셔플 유닛
마스크 레지스터에 의해 AVX-512는 SOA를 효율적으로 처리할 수 있습니다. 그러나 메모리에 저장된 원본 데이터 배열이 AOS라면 SOA로 바꿔줘야 합니다. 이 변환에는 집결/분산 명령을 쓸 수도 있지만 그럼 대기 시간이 길고 메모리 액세스가 복잡해집니다. 그래서 로드/셔플을 조합한 방법이 나왔습니다.
이 레이아웃 변환 과정에서 셔플을 여러번 수행합니다. 변환 과정에서 셔플이 연속 실행되며 셔플 엔진의 사용이 늘어납니다. 서니 코브의 AVX-512 유닛 배치는 이런 요구에 맞추기 위함이라 추측됩니다. 정리하면 AVX-512F에서 확장된 AOS-SOA 변환 명령을 살리기 위해 서니 코브는 AVX-512 명령도 확장하고, 그 결과 AVX-512를 보다 효율적으로 쓰리라 기대됩니다.