CPU는 메모리에 있는 데이터를 가져와서 연산해야 한다. 이때 메모리를 가져오는 과정에서 CPU가 쉬게되면 이는 손해다. 따라서 캐시가 도입되었다.

image.png

image.png

코어마다 L1, L2캐시가 붙어있고 L3캐시를 공유하는것을 확인할 수 있음.

image.png

아래는 2차원 배열을 두가지 방식으로 반복해서 읽어, 캐시의 공간적 지역성 효과를 비교하는 예제.

//

#include <iostream>
#include <ctime>
#include <cstddef>
#include <windows.h>

int main()
{
    const std::size_t rows = 8000;
    const std::size_t cols = 8000;
    std::size_t total = rows * cols;

    int* a = new int[total];
    if (a == NULL)
    {
        std::cerr << "allocation failed\\n";
        return 1;
    }

    for (std::size_t i = 0; i < total; ++i)
    {
        a[i] = 1;
    }

    volatile long long sum = 0;
    std::clock_t t0, t1;
    int iter = 0;

    while (1)
    {
        ++iter;
        // 행 우선 접근 (row-major) — 캐시 친화적
        t0 = std::clock();
        for (std::size_t i = 0; i < rows; ++i)
        {
            std::size_t base = i * cols;
            for (std::size_t j = 0; j < cols; ++j)
            {
                sum += a[base + j];
            }
        }
        t1 = std::clock();
        double row_ms = (double)(t1 - t0) * 1000.0 / CLOCKS_PER_SEC;

        // 열 우선 접근 (col-major) — 캐시 비친화적
        sum = 0;
        t0 = std::clock();
        for (std::size_t j = 0; j < cols; ++j)
        {
            for (std::size_t i = 0; i < rows; ++i)
            {
                sum += a[i * cols + j];
            }
        }
        t1 = std::clock();
        double col_ms = (double)(t1 - t0) * 1000.0 / CLOCKS_PER_SEC;

        std::cout << "iter=" << iter
            << " row-major(ms): " << row_ms
            << " col-major(ms): " << col_ms
            << " sum=" << sum << std::endl;

        Sleep(1000);
    }

    delete[] a;
    return 0;
}