본문 바로가기
카테고리 없음

[기획 연재 7편] 배열(Array)의 물리학: 왜 데이터는 붙어 있어야 하는가? (캐시 메모리의 비밀)

by khhjyc_ 2026. 1. 17.

서론: 수만 개의 별을 관리하는 법

우주에는 수조 개의 별이 있습니다. 제가 NASA에서 항성 데이터를 분석할 때, 별 하나하나에 star1, star2... 이렇게 이름을 붙여서 변수를 만들었다면 어떻게 되었을까요? 아마 코드를 짜다가 수명을 다했을 것입니다.

우리는 이 문제를 해결하기 위해 배열(Array)이라는 도구를 사용합니다. 하지만 많은 입문자가 배열을 단순히 '변수를 모아놓은 서랍장' 정도로만 이해합니다.

공학자의 관점에서 배열의 진정한 가치는 '모음'이 아니라 '연속성(Contiguity)'에 있습니다. 오늘 7편에서는 배열이 물리적으로 메모리에 어떻게 배치되는지, 그리고 왜 데이터를 다닥다닥 붙여 놓는 것이 속도의 혁명을 가져오는지 그 비밀을 파헤쳐 봅니다.

본론 1: 기차처럼 연결된 메모리

배열 선언 int arr[5];는 컴퓨터에게 이런 명령을 내리는 것과 같습니다.
"메모리야, 흩어져 있는 방 5개를 주지 말고, 반드시 옆으로 나란히 붙어있는 방 5개를 통째로 줘!"

  • 일반 변수: 각자 다른 주소에 흩어져 사는 '단독 주택'입니다.
  • 배열: 한 건물에 호수별로 붙어 있는 '아파트'입니다.

이것은 매우 중요한 물리적 차이를 만듭니다. 배열은 데이터들이 물리적으로 빈틈없이 이어져 있기 때문에, 시작점의 주소만 알면 나머지 데이터의 위치도 수학적으로(인덱스) 즉시 계산할 수 있습니다.

본론 2: 배열의 이름은 곧 포인터다

지난 6편에서 배운 포인터를 기억하시나요? 놀랍게도 C언어에서 '배열의 이름'은 그 자체가 '첫 번째 칸의 주소'를 가리키는 포인터입니다.

arr이라는 이름은 &arr[0](0번 칸의 주소)과 완전히 똑같습니다.
우리가 arr[2]라고 쓰는 것은, 사실 컴퓨터 내부에서 "시작 주소(arr)에서 2칸만큼 떨어진 곳(*)"을 찾아가라는 포인터 연산을 수행하는 것입니다.

즉, 배열과 포인터는 배다른 형제가 아니라, 동전의 양면과 같은 존재입니다. 이 원리를 깨닫는 순간, C언어의 자료구조가 한눈에 들어오기 시작합니다.

본론 3: 속도의 비밀, 캐시 적중률(Cache Hit)

이것이 오늘 이야기의 핵심이자, NASA가 배열을 사랑하는 이유입니다. CPU는 램(RAM)보다 훨씬 빠릅니다. 그래서 CPU는 램에서 데이터를 가져올 때, 낭비를 줄이기 위해 요청한 데이터뿐만 아니라 '그 옆에 있는 데이터'까지 뭉텅이로 미리 가져옵니다(Prefetch). 이를 캐시(Cache)라고 합니다.

공간 지역성(Spatial Locality)의 마법

만약 데이터가 배열처럼 연속적으로 붙어 있다면?
CPU가 0번 칸을 읽을 때, 1번, 2번, 3번 칸도 캐시 메모리에 함께 딸려 들어옵니다. 이후 CPU가 1번 칸을 찾을 때는 느린 램을 거치지 않고, 이미 도착해 있는 빠른 캐시에서 즉시 꺼내 씁니다. 이를 '캐시 히트(Cache Hit)'라고 합니다.

반면 데이터가 여기저기 흩어져 있다면? CPU는 매번 느린 램에 접속해야 하고, 성능은 급격히 떨어집니다. 이것이 바로 배열이 가장 빠른 자료구조인 과학적 이유입니다.

결론: 하드웨어의 특성을 이용하라

지금까지 배열(Array)이 단순한 목록이 아니라, 컴퓨터 하드웨어의 특성(캐시 메모리)을 극대화하기 위해 고안된 공학적 걸작임을 살펴보았습니다.

개발자는 단순히 기능을 구현하는 것을 넘어, 하드웨어가 데이터를 어떻게 좋아하는지를 이해해야 합니다. 데이터를 가지런히 정렬해 주는 것(배열)만으로도 여러분의 프로그램 속도는 비약적으로 빨라질 수 있습니다.

다음 [Part 8. 함수(Function)와 모듈화] 편에서는, 수만 줄의 코드를 레고 블록처럼 분해하고 조립하는 '함수'의 설계 미학에 대해 다뤄보겠습니다. 거대한 우주선을 만드는 엔지니어링의 정수(Essence)를 기대해 주십시오.