원문 캡처 · hackaday.com
원문 캡처 · hackaday.com

프로그램은 메모리를 주소가 붙은 바이트 배열처럼 다루지만, 실제 컴퓨터에서는 데이터가 여러 단계의 캐시와 가상 메모리, 저장장치를 거칠 수 있다. 이 구조를 이해하면 메모리 접근 성능이 달라지는 이유와 리눅스의 압축 메모리 프로젝트 CRAM이 제안하는 방식을 파악할 수 있다.

프로세서는 한 클록 주기에 여러 명령을 실행할 수 있지만, 일반적인 DRAM에 접근하려면 수백 주기가 걸릴 수 있다. 이를 줄이기 위해 프로세서 가까이에 더 작고 빠른 메모리를 두고 자주 쓰는 데이터를 복사해 둔다. 현대 데스크톱 프로세서에는 대개 L1·L2·L3 캐시가 있으며, 여러 코어가 각자 L1·L2를 쓰고 L3를 공유하는 구성이 흔하다. 다만 캐시 구성은 프로세서 아키텍처마다 다르다.

캐시는 보통 바이트 하나씩이 아니라 캐시 라인 단위로 데이터를 옮기고 추적한다. 현대 데스크톱 프로세서에서는 캐시 라인이 흔히 64바이트다. 캐시에 없는 바이트를 읽으면 프로세서는 대개 그 바이트가 속한 라인 전체를 가져온다. 다음 바이트도 캐시에 있을 가능성이 있어 순차 접근은 무작위 접근보다 빠를 수 있으며, 하드웨어 프리페처가 다음에 필요한 데이터를 미리 예상하기도 한다.

서로 다른 코어가 논리적으로 별개인 변수를 수정하더라도 두 변수가 같은 캐시 라인에 있으면 코어끼리 상대방의 복사본을 무효화하느라 시간을 쓸 수 있다. MESI 같은 프로토콜은 캐시 라인의 소유권과 복사본의 유효성을 조정한다. 하지만 캐시 일관성이 멀티스레드 프로그램의 동기화나 메모리 순서 규칙을 대신하지는 않는다.

프로그램이 쓰는 주소도 보통 RAM의 물리 주소가 아니다. 운영체제와 프로세서는 가상 메모리를 통해 프로그램별 가상 주소 공간을 물리 메모리에 연결한다. 메모리 관리 장치(MMU)는 페이지 테이블을 이용해 주소를 변환한다. 이 방식은 서로 다른 프로그램이 같은 가상 주소를 써도 충돌하지 않게 하고, 메모리 보호와 물리 페이지 공유를 가능하게 한다.

주소 변환에도 비용이 들기 때문에 프로세서는 최근 변환 정보를 저장하는 변환 색인 버퍼(TLB)를 둔다. TLB에 필요한 정보가 있으면 페이지 테이블을 따라가는 작업을 피할 수 있다. 일반적인 CPU 캐시가 캐시 라인 단위로 작동하는 것과 달리, 가상 메모리는 대개 페이지 단위로 관리한다. 페이지 크기는 흔히 4KB지만 더 큰 페이지도 지원한다.

여러 프로세서나 일부 멀티코어 시스템에서는 메모리 접근 시간이 위치에 따라 달라질 수 있다. 비균일 메모리 접근(NUMA) 시스템에서는 프로세서가 자기 NUMA 노드에 연결된 메모리에 더 빠르게 접근하고, 다른 노드의 메모리에 접근할 때는 인터커넥트를 거칠 수 있다. 운영체제는 메모리를 이를 사용하는 프로세서 가까이에 두려 한다. 따라서 프로그램 주소가 바뀌지 않아도 스레드를 다른 CPU로 옮기면 메모리 성능이 달라질 수 있다. 칩렛, 이기종 메모리, CXL 같은 기술도 메모리 배치를 더 복잡하게 만든다.

가상 페이지가 접근 시점에 RAM에 없을 수도 있다. 프로그램이 현재 사용할 수 없는 페이지에 접근하면 페이지 폴트가 발생하고, 운영체제는 새 페이지를 할당하거나 실행 파일·메모리 매핑 파일에서 데이터를 가져오거나 스왑 공간에서 페이지를 복구한다. 실행 파일의 데이터처럼 변경되지 않는 페이지는 보통 스왑으로 내보내지 않고 버린 뒤 필요할 때 다시 불러온다. SSD 접근은 RAM보다 훨씬 느리므로 스왑이 과도하면 컴퓨터 성능이 크게 떨어질 수 있다. 다만 가상 메모리는 주소 변환과 보호 기능만으로도 유용하며, 페이지를 디스크로 옮기는 스왑이 반드시 필요한 것은 아니다.

파일을 읽을 때도 운영체제가 매번 SSD에서 데이터를 가져오는 것은 아니다. 파일 시스템 페이지 캐시는 최근 또는 자주 접근한 파일 데이터를 RAM에 보관하고, 쓰기 작업도 영구 저장장치에 기록되기 전까지 RAM에 버퍼링할 수 있다. RAM이 보조 저장장치의 캐시처럼 작동하는 셈이다. SSD 자체에도 캐시와 주소 변환이 있다. SSD의 플래시 변환 계층은 논리 블록 주소를 실제 플래시 위치에 대응시켜 마모 평준화와 가비지 컬렉션을 호스트 운영체제에서 감춘다.

리눅스의 압축 메모리와 CRAM

RAM을 늘리지 않고 더 많은 데이터를 담는 방법으로 메모리 압축을 활용할 수 있다. 리눅스의 zram은 RAM 안에 압축 블록 장치를 만들며, 흔히 스왑 장치로 쓰여 페이지를 저장장치에 기록하는 대신 압축된 형태로 보관한다. zswap은 스왑 장치 앞에 놓이는 압축 캐시다. 리눅스가 페이지를 스왑하려 할 때 zswap은 이를 압축해 RAM에 보관하고, 필요하면 페이지를 뒤에 있는 스왑 장치로 내보낸다.

두 방식은 프로세서 시간을 사용해 메모리 용량을 늘리고 저장장치 입출력을 줄인다. 페이지 압축 해제에는 CPU 주기가 들지만 SSD에서 데이터를 가져오는 것보다는 훨씬 저렴할 수 있다. 다만 두 방식 모두 스왑 하위 시스템을 거친다. 압축된 페이지가 필요해지면 프로그램이 접근하기 전에 일반 페이지로 압축을 풀어야 하므로 페이지 폴트, 메모리 관리 오버헤드, 데이터 이동이 발생한다.

메타의 그레고리 프라이스(Gregory Price)가 2026년 10월 Linux Plumbers Conference에서 소개한 CRAM(Compressed RAM)은 압축 메모리를 일반적인 스왑 장치 대신 특수 NUMA 노드로 노출하는 프로젝트다. 리눅스는 기존 메모리 관리 기능을 사용해 일반 DRAM과 압축 메모리 사이에서 페이지를 옮길 수 있다. 개발진은 하드웨어 지원 압축·압축 해제를 통해 기존의 페이지 단위 압축 스왑보다 더 세밀한 단위로 접근하는 방식을 제안한다.

개발진은 일부 테스트에서 일반 DRAM에 가까운 읽기 성능을 보였고, 특정 작업 부하에서는 zram보다 크게 나은 벤치마크 결과를 보고했다. 그러나 압축이 비용 없이 이뤄지는 것은 아니다. 쓰기 성능, 압축률, 하드웨어 요구 사항, 압축되지 않는 데이터를 처리하는 문제가 남아 있다. CRAM은 아직 개발 중이며 일상적인 사용을 위한 단계는 아니다. Phoronix의 CRAM 소개

메모리 성능이 중요할 때는 이런 추상화의 세부 사항이 드러난다. 순차 접근은 무작위 접근보다 훨씬 빠를 수 있고, 서로 다른 변수를 수정하는 스레드도 같은 캐시 라인을 두고 충돌할 수 있다. 프로세스를 다른 CPU로 옮기면 메모리 지연 시간이 달라질 수 있으며, 일반 메모리처럼 보이는 페이지를 실제로는 압축 해제하거나 SSD에서 가져와야 할 수도 있다. MMU나 복잡한 캐시가 없는 소형 임베디드 시스템에서는 메모리가 처음 배운 물리 주소 공간에 더 가까울 수 있다.