CXL : Compute Express link — New interconnect interface for high performance device communication
CPU는 GPU나 FPGA 가속기 같은 주변 디바이스들과 상호작용하며 동작하는데, 기존에 디바이스들은 PCI Express라고 불리는 인터페이스를 통해 연결되어 동작한다. 하지만 새로운 어플리케이션 트렌드(Machine learning, Big data Analytics, Edge computing)의 등장에 따라 디바이스간에 효율적인 데이터 전송과 더 빠른 메모리 접근 방식을 요구하게 되었는데 이 PCI Express의 낮은 대역폭과 높은 지연 시간이 디바이스간 통신에 병목이 되고 있다.
따라서 각 디바이스의 메모리에 빠르고 효율적으로 접근하면서도 Cache Coherence까지 보장하는 형태의 새로운 인터페이스 규격이 필요하게 되었고 이를 위해 CXL이라는 새로운 인터페이스가 개발되었다.
배경 지식
1. PCI Express
CXL의 경우 PCI Express의 일부분을 이용한다. 또한 기존의 연결 방식들 모두 PCI Express를 이용하기 때문에 우선 PCI Express 의 구조를 알아보도록 하자. PCI Express는 CPU와 디바이스들간의 통신을 위한 프로토콜로 다음과 같이 3개의 계층으로 이루어져있다.
Transaction layer : PCI 기기들 간에 주고받은 명령 형태인 transaction을 생성하고 처리한다. 트랜잭션의 종류는 다음과 같이 크게 4개다.
- 메모리 트랜잭션
디바이스의 메모리 주소에 매핑되어 R/W 연산 요청 - I/O 트랜잭션
I/O 주소 공간에 매핑 매핑되어 R/W 연산 요청 - Configuration 트랜잭션
디바이스 함수 설정 및 셋업, 초기화 작업에 이용됨 - 메시지 트랜잭션
이벤트 시그널링 및 일반적인 메시징에 이용
Data Link Layer : Transaction layer에서 생긴 패킷 전달에 신뢰성 보장 등의 역할을 한다.
Physical Layer : 만들어진 패킷을 실제 두 디바이스 간에 전기 신호로 전달하는 역할을 한다.
2. cache coherence
보통 프로세서는 R/W를 메모리가 아닌 cache에 대고 진행한다. 한 시스템에 여러개의 프로세서가 존재하고, 이 프로세서들이 자신 만의 로컬 캐시를 가지고 다른 여러 프로세서들과 메모리를 공유하고 있을 때, 캐시의 갱신으로 인한 데이터 불일치 문제가 발생한다. 예를들어 두 프로세서1,2가 변수 x에 대한 값을 캐쉬에 가지고 있는데 프로세서1이 자신의 캐쉬 값을 갱신하면 다른 프로세서의 캐쉬값은 그대로 예전 값 이기 때문에 프로세서2가 값을 읽을 때는 예전 값을 읽게 된다. 이런 캐쉬 일관성을 맞추는 것이 cache coherence라고 하고 어떤 메모리에 접근할 때 cache 일관성을 고려하지 않고 접근할 수 있도록 보장하는 접근을 cache coherent access라고 한다.
여러 디바이스가 서로 메모리에 접근해야 하는 경우 같은 CPU 내부의 코어들 간의 캐쉬 일관성 뿐 아니라. 각 디바이스들간의 캐쉬 일관성또한 보장 되어야한다. CXL의 캐쉬 일관성 프로토콜을 이용해 각 디바이스와 CPU간의 효율적이고 빠른 캐쉬 일관성 접근을 가능하게 한다.
cache coherent를 맞추는 방법에는 많은 알고리즘이 있지만 CXL에서는 MESI protocol을 사용한다. 이 프로토콜은 각자 캐시라인에 상태 비트를 추가하여 연결된 caching agent가 캐쉬 갱신, 접근에 따라 다른 캐쉬에 일관성에 대한 정보를 전파하는 형태의 프로토콜이다.
각 상태는 다음과 같이 4가지로 나뉘고, 자기자신과 다른 프로세서의 R/W에 따라 상태가 바뀐다.
Invalid — 더 이상 유효하지 않은 캐쉬이다. 메모리에서 다시 읽어와야한다.
Shared — 나 말고 다른 프로세서, 또는 디바이스가 같은 데이터를 캐슁중이다.
Modified — 현재 값은 갱신된 값이다. 즉 write back이 필요하다.
Exclusive — 오직 나만이 해당 데이터를 캐슁중이다.
아래 상태 전이표는 프로세서들 간의 동작에 따른 캐쉬 상태 변화를 나타낸다.
3. 기존 CPU GPU interconnect
아래 사진은 기존의 CPU/GPU 아키텍쳐의 구조를 도식화한 것이다. CPU와 GPU처럼 서로 다른 아키텍쳐의 프로세서들간의 상호작용으로 동작하는 컴퓨팅 시스템을 (이기종 컴퓨팅)heterogeneous computing 이라고 한다. Heterogeneous computing은 서로 다른 연산에 최적환 프로세서들이 서로 데이터를 주고받으며 컴퓨팅 하는 것이 관건인데, 이 때문에 서로 다른 디바이스간 데이터 전송과 메모리 접근이 성능을 높이는데 매우 중요하다.
각 디바이스는 자기 자신의 메모리에 접근은 매우 빠르나 서로 다른 디바이스의 메모리에 접근하는 것은 매우 느리다. 다른 디바이스의 메모리에 접근하기 위해서는 PCI Express 인터페이스를 거쳐야 하기 때문인데, 평균적으로 CPU의 메모리 접근 대역폭은 약 100GB/s, GPU의 HBM 메모리 대역폭은 약 730GB/s 이나 PCI Express의 대역폭은 64GB/s 밖에 되지 않는다.
CPU 간 연결의 경우 QPI라는 인터페이스를 이용한다. 이 QPI를 통해 각 CPU간의 캐쉬 일관성을 보장하는 메모리 접근이 가능하다. 하지만 CPU와 GPU는 PCI Express를 이용해 데이터를 주고 받아야하기 때문에 매우 느리다.
먼저 각 디바이스들이 메모리에 접근하는 과정을 살펴보자.
Device R/W data from Host memory
디바이스가 호스트 메모리를 읽기 위해서는 PCIe DMA를 이용해야 한다. OS가 먼저 디바이스가 읽어갈 수 있도록 메모리를 할당해 놓으면 해당 영역을 디바이스가 DMA를 통해 읽어가는 형태이다.
이렇게 OS가 디바이스를 위해 할당한 영역은 page-lock memory라고 불리는데, OS가 주변 디바이스들이 DMA를 통해 직접 접근을 허용한 메모리 영역이다. 이렇게 할당된 메모리는 DISK로 evict 되지 않는다. CPU의 연산과는 독립적으로 디바이스가 메모리에 접근할 수 있으므로 race condition이 발생할 수 있다.
CUDA API에 있는 cudaMallocHost 함수가 대표적으로 page-locked memory를 할당하는 함수다. 할당된 호스트 메모리에 GPU가 직접 접근하여 연산을 수행할 수 있다. 캐쉬 일관성의 경우 디바이스가 DMA를 수행하기 전/후로 하드웨어 로직이 알아서 캐시의 invalidate 및 clean 등을 수행하는 형태가 있고, 캐쉬 일관성을 보장하지 않는 형태가 있다. 이러한 경우 소프트웨어적으로 처리해주어야 한다
이 DMA 디바이스가 이용하는 PCI Express가 병목이 될 수 있다.
CPU R/W from device memory
다른 PCI Express로 연결된 디바이스와 마찬가지로 PCI Express의 Transaction을 이용하여 GPU의 메모리에 접근할 수 있다. 디바이스의 PCI configuration space를 통해 해당 디바이스의 메모리와 레지스터에 접근할 수있는 주소를 알아내고, PCI Express 규격에 맞는 Transaction type을 생성해 요청을 전송한다.
이렇게 양측의 데이터를 주고받기 위해서는 PCI Exprerss를 이용하고 다른 연결에 비해 상대적으로 낮은 대역폭을 가진 PCI Express가 병목이 될 수 있다.
4. CXL 등장 배경
기존의 CPU와 주변 디바이스 간의 연결은 위에 설명한것 처럼 PCI로 연결된다. 하지만 GPU나 가속기 같은 FPGA 디바이스같은 큰 데이터를 처리하는 디바이스들이 점차 많이 사용됨에 따라 이 PCI가 병목이 되고있다.
- GPU → CPU 메모리 접근의 경우 CPU가 page-locked로 할당한 메모리를 PCIe DMA를 통해서 접근하는데 이 PCI가 병목이 된다.
- CPU → GPU 메모리 접근의 경우 마찬가지로 다른 PCIe 디바이스 처럼 PCIe 패킷을 전송하여 데이터를 주고받는데 이 또한 PCI 프로토콜이 병목이다.
따라서 다른 디바이스에 있는 메모리에 빠르게 접근할 수 있으면서도 디바이스 또는 프로세서에서 기록한 내용이 또 다른 프로세서 또는 디바이스에 의해 언제든 읽힐수 있도록 캐쉬 일관성을 보장하는 새로운 메커니즘이 필요하다.
CXL
1. CXL 구조
CXL은 인텔에서 새롭게 개발한 디바이스 간 연결 인터페이스다. PCI의 physical layer는 그대로 사용하고 새롭게 data link layer 와 transaction layer를 개발하여 사용한다.
왼쪽은 CXL, 오른쪽은 기존의 PCI Express layer다.
Transaction layer / Link Layer
디바이스를 초기화 하고 검색하는 프로토콜은 기존에 PCI Express에서 사용하던 것을 그대로 사용하고, 서로 간에 메모리 접근에 관한 프로토콜을 새롭게 개발하였다.
CXL의 Transaction layer는 크게 3가지로 구분된다.
- CXL.cache — 디바이스의 메모리를 참조할 때 다른 연결된 디바이스 및 코어의 캐쉬 일관성을 관리하는데 사용한다.
- CXL.mem — CPU가 device의 메모리에 접근하는데 관련된 프로토콜이다.
- CXL.io — 시스템 자치 검색, 인터럽트 세팅, 디바이스 초기화, 엑세스 정보 제공 등의 역할을 하는 프로토콜이다. 기존 PCI Transaction에 있는 기능과 동일하다.
Physical Layer
기존 PCI Expresss의 Physical layer를 그대로 이용한다.
2. CXL coherence protocol
CXL의 Coherence protocol은 위 그림과 같이 여러곳에서 동작한다.
CXL.cache는 기존의 캐쉬 관리 로직처럼 다른 CXL 디바이스의 캐쉬 일관성 문제를 해결해준다. 호스트의 캐싱 에이전트에 구현되어 있다. CXL.memory는 home agent의 DDR처럼 사용할 수 있도록 device memory를 추상화 해준다. 즉 CPU는 CXL.mem을 통해 write back cache처럼 디바이스 메모리를 사용할 수 있다. 호스트의 홈 에이전트에 구현되어있다.
기존의 QPI나 UPI같은 캐쉬 일관성 프로토콜들은 각자 호스트와 디바이스가 각각 캐싱 에이전트를 가지고 있고 이 캐싱 에이전트들이 서로의 캐쉬를 관리한다. 왼쪽 그림과 같이 서로 간의 캐쉬 일관성을 맞추기 위해 두 캐슁 에이전트가 통신하면서 캐쉬 일관성을를 맞춘다. 이러한 형태의 구조는 고대역폭의 빠른 전송계층을 이용해야만 병목 현상없이 데이터를 주고받을 수 있는데 CXL에서는 PCI Express의 물리 전송계층을 그대로 사용하는 대신 CXL은 호스트의 CXL 캐슁 에이전트에 캐쉬 일관성 프로토콜을 전권 위임한다.
CXL.Cache
디바이스가 호스트의 메모리에 접근할 때 이 캐쉬 일관성을 보존하기 위한 프로토콜이다. 프로토콜은 총 15개의 Transaction 타입으로 구성되어있으며 대부분의 로직이 호스트쪽 캐슁 에이전트에 구현되어있다. 디바이스는 호스트에게 요청과 응답을 할 수 있으며, 호스트는 연결된 캐쉬들에게 Snoop 요청을 보낸다. Snoop이란 캐쉬의 상태 변경을 지시하는 요청이다. 즉 호스트가 캐쉬 일관성을 관리한다.
아래 예시를 통해 동작을 파악해보자. 아래 그림의 화살표는 모두 CXL의 단위 트랜잭션 들이다.
read
- CXL 디바이스의 캐쉬가 Invaild 상태이다. 따라서 데이터를 메모리로부터 읽어오기 위해 호스트에 있는 caching agnet에 요청을 보낸다. RdShared 트랜잭션을 통해 다른 Peer Cache의 데이터를 Sharing 하면서 가져온다.
- 호스트 캐슁 에이전트는 연결된 다른 Peer Cache들의 캐쉬 일관성을 해결하고 메모리로 부터 데이터를 읽어들인다.
- 마지막으로 요청한 CXL 디바이스에 캐쉬 상태 변화를 지시하고 캐쉬 일관성이 해결되면 데이터를 전송한다.
위 그림의 Peer Cache와 Memory Controller가 될 수 있는 것들을 아키텍쳐에 표시해보았다.
Peer cache는 현재 호스트 코어의 캐쉬, 다른 코어의 캐쉬, 다른 디바이스의 캐쉬가 될 수 있다. 즉 CXL.cache 프로토콜은 해당 디바이스 뿐 아니라 호스트, 다른 디바이스에 캐쉬되어 있는 데이터 모두의 캐쉬 일관성을 관리해준다. 또 호스트의 메모리 뿐 아니라 다른 디바이스의 메모리 또한 CXL.cache를 통해서 읽어올 수 있는데 CXL.memory가 호스트의 메모리 뿐 아니라 다른 디바이스의 메모리 접근을 가능하게 해주기 때문이다.
write
- Invalid 상태기 때문에 디바이스 캐쉬에서 write hit가 되지 않는다. 즉 메모리에서 값을 가져와야한다.
- RdOwn 트랜잭션을 통해 다른 Peer Cache의 캐쉬를 모두 invalid하게 만들고 자신에게 가져와 Exclusive 상태로 만든다.
- 그 후 자신의 캐쉬에만 데이터를 업데이트하고 Modified 상태로 변경한다.
- 추후에 캐쉬의 write back이 필요한 경우 dirtyEvict 트랜잭션을 전송하고 호스트는 해당 디바이스 캐쉬를 Invalid 상태로 변경한 뒤 해당하는 메모리에 값을 적는다.
위 두가지 예시에 따라 디바이스가 호스트, 또는 다른 디바이스의 메모리를 읽으려고 할 때 어떤 방식으로 캐쉬 일관성과 데이터 이동이 처리되는지 알아보았다. 연결된 모든 디바이스와 호스트의 캐쉬는 호스트에서 관리하며 이 호스트의 프로토콜이 CXL.cache이다.
CXL.memory — CPU가 device 의 메모리에 접근하기 위한 프로토콜
CPU, 즉 호스트가 연결된 디바이스 들의 메모리에 접근하기 위한 프로토콜이다. 호스트의 홈 에이전트에 구현되어 있으며, 이를 통해 CPU가 다른 디바이스의 메모리에 접근하는 것을 기존에 write back 메모리에 접근하는 것 처럼 추상화 시켜준다. 캐쉬 일관성은 CPU 캐쉬 일관성 로직과 CXL 캐슁 에이전트가 관리해준다.
해당 프로토콜에는 다음과 같은 형태의 트랜잭션이 존재한다.
H2D : M2S Request (Req), Request w/ Data (RwD)
D2H : Non-Data Response (NDR), Data Response (DRS)
즉 CPU는 디바이스 메모리에 있는 데이터와 자기 자신의 메모리에 있는 데이터 모두 기존의 캐쉬에 복사하여 사용하면서, 디바이스 메모리와 자기 자신 메모리 모두 write back memory 처럼 접근 가능하다.
Biased coherency
CPU는 위 설명대로 CXL 캐싱 에이전트에 구현 된 CXL.cache를 통해 기존에 캐쉬에 접근 하던대로 접근할 수 있으며 홈 에이전트에 구현된 CXL.memory 프로토콜을 통해 디바이스 메모리와 자기 자신의 메모리 모두 write back 메모리처럼 사용할 수 있다.
디바이스의 경우 CXL.cache 프로토콜을 통해 다른 디바이스나 호스트의 메모리에 접근할 수 있다. 하지만 CXL의 캐쉬 일관성 프로토콜의 구현 구조는 비대칭성 구조로 캐슁 에이전트는 호스트에만 존재한다. 따라서 디바이스는 자기 자신의 메모리에 접근할때에도 호스트의 도움을 받아야 캐쉬 일관성을 보장하는 접근이 가능하다.
따라서 디바이스는 bias table 이라는 bit를 관리하고 이 bit의 값에 따라 자신의 메모리에 접근하는 방법이 다르다. bias table이 1이면 다른 곳에 자신 메모리의 카피가 존재, 아니라면 Exclusive 하게 가지고있다는 뜻이다.
1일때를 host bias, 0일때를 device bias라고 부른다. 아래 그림은 각 bias table 상태에 따른 메모리 접근 방식을 도식화한 것이다.
Bias=host → 자신의 메모리를 읽기 위해서는 다른 곳에 있는 데이터 카피의 일관성을 맞추어야한다. 우선 CXL.cache의 RdOwn 호출 하고. 호스트의 캐슁 에이전트에 연결된 Peer Cache들의 일관성을 보장한한 뒤 디바이스 메모리로 요청을 포워딩한다.
bias=device → 호스트나 다른 디바이스에 해당 데이터의 카피본이 없다. 즉 캐쉬 일관성 문제에 신경쓰지않고 바로 읽어올 수 있다.
요약
CPU 입장 :
CPU 는 캐슁 에이전트와 이에 구현된 CXL.cache 프로토콜로 디바이스와 자기 자신의 메모리 캐쉬를 관리하고 디바이스 메모리와 자기 자신의 메모리 모두 write back 메모리 처럼 접근 가능하다.
디바이스 입장 :
CPU 메모리 접근 시 →CPU에 있는 메모리는 호스트의 캐싱 에이전트에 구현된 (CXL.cache)를 통해 접근가능 호스트의 캐슁 에이전트가 직접 디바이스 내부 캐쉬와 자신의 캐쉬를 CXL.cache를 통해 관리함. 자기 메모리 접근 시 Host biased냐 device biased냐에 따라 다름.
기존에 CPU가 디바이스 메모리 접근 시 PCIe를 통해 디바이스 메모리 컨트롤러에 요청하면 이 값을 가져오는 형식, CXL을 사용하면서 디바이스 메모리도 write back memory처럼 사용 가능하게 되었다.
기존에 디바이스가 host memory 접근 시 PCIe DMA를 통해 호스트 메모리에 접근해야 했으나 CXL을 이용하면 기존에 자기 메모리에 접근하듯이 load/store 명령어로 접근 가능하게 되었다.