Home C# [C#]캐시 메모리

[C#]캐시 메모리

by DUBUKIMCH

C# 개발자 입장에서 **캐시 메모리(Cache Memory)**는 직접 구현하거나 제어하진 않지만, 성능 최적화와 관련된 중요한 개념입니다. 아래에 캐시 메모리의 동작 원리부터 정책, 매핑까지의 개념을 설명하고, 가능한 경우 C# 코드 또는 .NET의 관점에서 어떻게 반영되거나 유추되는지까지 설명하겠습니다.


🧠 캐시 메모리란?

CPU가 **주기억장치(RAM)**보다 빠른 속도로 데이터를 처리하기 위해 사용하는 고속 임시 저장 공간입니다.

C#에서는 직접 접근은 불가능하지만, 반복적인 배열 접근, 딕셔너리 캐시 구현, 웹 캐싱, JIT 최적화 등에 간접적으로 캐시의 영향을 받을 수 있습니다.


1. ⚙️ 캐시 메모리 동작 원리

  • CPU가 어떤 주소의 데이터를 요구
  • 캐시에 있으면 캐시 히트(hit) → 빠르게 반환
  • 없으면 캐시 미스(miss) → RAM에서 읽고 캐시에 저장 후 반환

📌 C# 간접 예시:

int[] arr = Enumerable.Range(0, 100000).ToArray();
long sum = 0;

for (int i = 0; i < arr.Length; i++)
{
    sum += arr[i];  // 연속된 접근 → 캐시 히트 ↑
}

arr가 연속된 메모리로 할당되므로, 공간 지역성에 따라 CPU 캐시 히트율이 높아집니다.


2. 📂 캐시 메모리 유형

유형설명
L1 Cache (Level 1)CPU Core에 가장 가까운 매우 빠른 캐시 (수십 KB)
L2 CacheL1보다 크고 느리며 여러 코어가 공유 가능
L3 CacheCPU 전체 또는 여러 코어 간 공유되는 캐시 (MB 단위)

🧩 C# 관점: 최적화 루프, 반복 참조, 구조체 정렬 등은 L1/L2 캐시에 영향을 줌


3. 📍 지역성(Locality)

종류설명
시간 지역성최근 접근된 데이터가 다시 사용될 가능성 ↑
공간 지역성접근한 데이터 근처의 데이터도 곧 사용할 가능성 ↑

📌 C# 예:

int[] arr = new int[100000];

for (int i = 0; i < arr.Length; i++)
    arr[i] = i; // 공간 지역성↑, 연속된 메모리 접근

for (int i = 0; i < arr.Length; i += 1024)
    arr[i] += 1; // 캐시 미스↑, 공간 지역성↓

4. 📌 캐시 교체 알고리즘

정책설명
LRU (Least Recently Used)가장 오래 안 쓴 항목 제거
FIFO (First In First Out)가장 먼저 들어온 항목 제거
LFU (Least Frequently Used)사용 빈도가 가장 낮은 항목 제거

📌 C# 사용자 캐시 구현 예 (LRU):

public class LRUCache<K, V>
{
    private readonly int capacity;
    private readonly Dictionary<K, LinkedListNode<(K key, V val)>> map;
    private readonly LinkedList<(K key, V val)> list;

    public LRUCache(int capacity)
    {
        this.capacity = capacity;
        map = new Dictionary<K, LinkedListNode<(K, V)>>();
        list = new LinkedList<(K, V)>();
    }

    public V Get(K key)
    {
        if (map.TryGetValue(key, out var node))
        {
            list.Remove(node);
            list.AddFirst(node); // 최근 사용으로 이동
            return node.Value.val;
        }
        return default!;
    }

    public void Put(K key, V value)
    {
        if (map.TryGetValue(key, out var node))
        {
            list.Remove(node);
        }
        else if (map.Count == capacity)
        {
            var last = list.Last!;
            list.RemoveLast();
            map.Remove(last.Value.key);
        }

        var newNode = new LinkedListNode<(K, V)>((key, value));
        list.AddFirst(newNode);
        map[key] = newNode;
    }
}

5. ✏️ 쓰기 정책 (Write Policy)

정책설명
Write Through캐시와 RAM에 동시에 씀 (일관성 ↑, 속도 ↓)
Write Back캐시에만 쓰고 나중에 RAM에 반영 (속도 ↑, 복잡성 ↑)

🧩 C# 예: MemoryCache vs WriteToFile

// Write Through 느낌
cache["user"] = userData;
File.WriteAllText("user.txt", JsonConvert.SerializeObject(userData));

// Write Back 느낌
cache["user"] = userData; // 디스크 반영은 이후 별도 처리

6. 📋 캐시 할당 정책

정책설명
Direct Mapping한 블록 → 하나의 캐시 라인
Fully Associative어떤 블록이든 아무 캐시 라인 가능
Set AssociativeN개의 캐시 라인 중 한 곳만 선택 가능 (현실적인 절충)

🧩 C#에서는 이런 하드웨어 캐시 할당을 직접 제어할 수는 없지만, 배열/구조체 정렬을 통해 캐시 친화적인 코드를 작성할 수 있음.


7. 🧼 캐시 플러시(Cache Flush)

  • 캐시의 내용을 비우거나 주기억장치로 강제 저장하는 작업
  • C#에서는 GC.Collect()나 파일 버퍼 flush 등으로 간접 제어

📌 예:

// 버퍼된 파일 쓰기 강제 반영
using var stream = new FileStream("log.txt", FileMode.OpenOrCreate);
byte[] data = Encoding.UTF8.GetBytes("log data");
stream.Write(data, 0, data.Length);
stream.Flush(); // 캐시 플러시

✅ 요약 정리표

항목설명C# 연관성 예시
캐시 동작 원리캐시 히트/미스를 통해 빠른 접근배열 반복, 반복 계산
캐시 유형L1, L2, L3최적화된 루프 구조
지역성시간/공간 지역성연속 배열 접근
교체 정책LRU, FIFO, LFU사용자 정의 캐시 구현
쓰기 정책Write Through, Write BackMemoryCache와 파일 저장 비교
할당 정책Direct, Fully, Set Associative구조체 정렬, 연속성 고려
캐시 플러시캐시 비우기 or 메모리 반영Flush(), GC.Collect()

🔚 마무리

C#에서 캐시 메모리는 하드웨어적으로 직접 다루지 않지만, 코드 작성 방식이 CPU 캐시 효율성에 큰 영향을 미칩니다.
또한, 소프트웨어 수준의 캐싱은 MemoryCache, Dictionary, 또는 LRU 캐시 구현을 통해 성능 최적화를 할 수 있습니다.

You may also like

Leave a Comment

error: Content is protected !!