가상 메모리와 CPU 캐시 구조까지 공부하고 나면 자연스럽게 한 가지 의문이 생긴다. “CPU는 메모리에 접근할 때 실제로 어떤 주소를 사용하는 걸까?” 나도 처음에는 프로그램이 사용하는 주소가 곧 실제 RAM 주소라고 생각했었다. 그런데 운영체제 구조를 조금 더 찾아보니까, 프로그램은 실제 메모리 주소를 직접 사용하는 게 아니라 가상 주소(Virtual Address)를 사용하고 있었고, CPU는 그걸 실제 물리 주소(Physical Address)로 계속 변환하면서 동작하고 있었다.
처음에는 “주소 변환 정도야 금방 되는 거 아닌가?”라고 생각했다. 그런데 현대 CPU는 워낙 빠르게 움직이다 보니까, 이 주소 변환 과정조차 반복되면 엄청난 성능 문제가 될 수 있었다. 그리고 그 문제를 줄이기 위해 등장한 구조가 바로 TLB(Translation Lookaside Buffer)다.

가상 메모리를 사용하면 주소 변환 과정이 계속 필요하다
운영체제는 프로그램마다 독립적인 메모리 공간을 제공하기 위해 가상 메모리를 사용한다. 쉽게 말하면 프로그램 입장에서는 “내가 메모리를 전부 사용하는 것처럼” 보이게 만드는 구조다. 실제로는 여러 프로그램이 하나의 RAM을 나눠 쓰고 있지만, 각 프로그램은 자기만의 주소 공간을 가진 것처럼 동작한다.
문제는 CPU가 메모리에 접근할 때마다 가상 주소를 실제 물리 주소로 변환해야 한다는 점이다. 이 변환은 페이지 테이블(Page Table)을 통해 이루어진다.
처음 이 개념을 봤을 때는 꽤 복잡하게 느껴졌다. 그냥 메모리 주소 쓰면 되는 줄 알았는데, CPU는 내부적으로 “이 가상 주소가 실제 RAM 어디를 가리키는지” 계속 확인하고 있었던 것이다.
나도 예전에 가상 메모리 구조 그림을 처음 봤을 때 “컴퓨터가 왜 굳이 이렇게 복잡하게 돌아가지?”라는 생각을 했었다. 그런데 조금 더 이해하고 나니까, 프로그램들이 서로 메모리를 침범하지 못하게 하고 안정성을 유지하려면 이런 구조가 꼭 필요하다는 걸 알게 됐다.
하지만 여기서 또 하나 문제가 생긴다. CPU는 메모리에 접근할 때마다 주소 변환까지 해야 하니까, 단순 계산보다 훨씬 많은 작업이 추가되는 것이다.
TLB는 주소 변환 결과를 미리 저장해두는 캐시다
CPU는 원래도 Cache Miss 때문에 RAM 접근 지연을 최대한 줄이려고 한다. 그런데 주소 변환까지 매번 페이지 테이블을 확인하면 성능이 더 떨어질 수밖에 없다. 그래서 CPU는 자주 사용하는 주소 변환 결과를 따로 저장해두는데, 이게 바로 TLB다.
쉽게 말하면 TLB는 “가상 주소 → 물리 주소” 변환 결과를 저장하는 초고속 캐시에 가깝다. CPU가 메모리에 접근할 때 먼저 TLB를 확인하고, 거기서 바로 주소를 찾으면 훨씬 빠르게 진행할 수 있다.
나도 처음에는 CPU 캐시만 있는 줄 알았는데, 알고 보니까 주소 변환 자체도 캐싱하고 있었다는 게 꽤 인상 깊었다. 현대 CPU가 왜 그렇게 복잡한 구조를 가지는지 조금씩 보이기 시작한 순간이었다.
특히 프로그램들은 비슷한 메모리 영역을 반복해서 사용하는 경우가 많기 때문에, TLB 적중률(TLB Hit)이 높으면 성능이 꽤 좋아진다고 한다. 반대로 필요한 주소 변환 정보가 TLB에 없으면 페이지 테이블까지 직접 가야 하고, 이 과정이 생각보다 큰 지연을 만들 수 있다.
예전에 CPU 구조 관련 글에서 “TLB Miss는 생각보다 비싼 비용이다”라는 표현을 본 적이 있었는데, 처음에는 과장이라고 생각했다. 그런데 페이지 테이블 접근 자체가 추가 메모리 접근을 의미한다는 걸 알고 나니까 왜 성능 문제가 되는지 조금 이해가 됐다.
TLB Miss가 많아지면 CPU 성능도 크게 흔들릴 수 있다
TLB는 굉장히 빠른 구조지만, 용량은 제한적이다. 그래서 너무 다양한 메모리 주소를 계속 사용하면 기존 정보가 밀려나고 TLB Miss가 자주 발생할 수 있다.
이 상황에서는 CPU가 페이지 테이블을 다시 확인해야 하고, 경우에 따라서는 여러 단계 메모리 접근이 추가된다. 결국 CPU는 또 기다리게 된다.
나도 처음 CPU 구조를 공부할 때는 “결국 CPU는 계속 기다림을 줄이기 위해 발전하는구나”라는 느낌을 받았다. 파이프라인, Branch Prediction, Out-of-Order Execution, Cache 구조까지 전부 결국은 CPU가 쉬지 않고 움직이게 만들기 위한 기술들이었다. TLB도 마찬가지였다.
특히 현대 CPU는 워낙 빠르기 때문에, 아주 작은 메모리 접근 지연도 전체 성능에 꽤 큰 영향을 줄 수 있다. 그래서 단순 계산 속도보다 “얼마나 효율적으로 데이터를 가져오느냐”가 훨씬 중요해진 것이다.
이걸 공부하면서 느낀 건, 우리가 평소 아무렇지 않게 사용하는 프로그램들도 내부에서는 엄청 복잡한 주소 변환과 캐시 구조 위에서 움직이고 있다는 점이었다.
한 줄로 정리하면 TLB는 가상 주소를 물리 주소로 변환한 결과를 저장해두는 초고속 캐시이며, CPU가 매번 페이지 테이블을 확인하지 않도록 만들어 메모리 접근 성능을 크게 높여주는 구조다.