Server programming

01_04_멀티쓰레드_캐시이론

devRiripong 2023. 3. 12.
반응형

Q1. 코어가 여러개면 어떤 문제가 발생하는지 식당에서 음료수 주문을 번복하는 것에 빗대어 설명해 보세요.

Q2. 무엇을 캐시할 것이냐에 대한 캐시철학 두 가지를 식당에서 일어나는 일에 빗대어 설명해 보세요.

Q3. arr[10000][10000] 배열에 1을 arr[x,y] 순서로 넣는 것과, arr[y,x] 순서로 넣는 것과 걸리는 시간 다른 이유가 무엇인지 설명해 보세요.

 

컴퓨터 구조 원리 : 캐시

손님이 몰려오다 보니 일처리 어떻게 할지 고민되기 시작했어.

주문 받은 내용을 주방에 전달해야 주문이 들어가.

주문 현황에다가 주문 받은 내용 기입하면 주방에서 확인해서 요리에 들어가는 구조로 되어 있다

주문 받아 주문현황판에 왔다 갔다 하는게 시간이 아깝다는 문제가 있어.

주문을 받자마자 주문 현황판에 기입하는게 아니라 모았다가 기입하는 꼼수를 생각해 낸다.

일단 단기 기억으로 기억하고, 벅차면 수첩에 모으고,

실제로도 이렇게 한다.

이 때 장점은 만약 2번 테이블에서 콜라를 시켰다가 사이다로 번복했다고 하면 아직 주문 현황판에 적지 않은 상태이기 때문에 콜라를 지우고 사이다로 바꿔치기 하면 자연스럽게 해결이 된다.

근데 직원이 여러명이 되면 문제가 발생한다.

수첩은 공유하지 않기 때문에 콜라 주문 했는데 다른 알바한테 사이다로 바꿔달라고 번복을 했을 경우. 두번째 알바는 콜라 받은 적 없고, 주문 현황판에도 콜라가 없어서 무슨말인지 못알아듣는 일이 발생한다. 알바마다 다른 정보 들고 있어서 혼선이 된다. 이게 컴퓨터에서도 일어나고 있어.

매번 RAM에 데이터 갱신하는 건 멀어서 힘들어.

레지스터, L1, L2 같이 단기기억, 수첩에 해당하는 게 존재한다. 캐시 장치에 일단 기입을 해서 다중에 메모리에 올리는 작업을 하게 된다.

캐시가 사용되는 건 내부적으로 계속 작동했던 거야.

무엇을 캐시할 것이냐에 대해 두가지로 분류한다.

 

싱글 스레드라 했으면 모르는 사이에 잘 일어 났지만 멀티스레드에서 발목을 잡는다.

 

멀티 스레드에서 각각의 코어로 실행되고 있고 코어마다 자신의 캐시가 있을텐데 만약 어떤 변수를 어떤 값으로 고쳤을 때 그게 100% 확률로 메모리에 올라가는게 아니니까 다른 스레드 입장에서 봤을 때 분명 첫번째 애가 데이터를 건드렸음에도 두번째 애는 방금 수정된 따끈따끈한 애로 보는게 아니라 이전 데이터 즉 자신이 기억하고 있던 캐시에 있던 데이터를 사용하게 된다는 문제가 있다.

 

로직적으로 데이터를 고쳤다면 고쳤다고 가정할 수 있지만 싱글스레드에서는 문제 없었지만 멀티스레드에서는 이 가정이 깨진다. 주문 현황판에 최종적으로 업데이트 한 다음에 두번째 아이가 사용하지 않으면 문제가 되는 상황이다.

 

짧은 실습을 해보자. 기존 작업을 날리고 간단한 테스트를 하자.

캐시가 잘 작동하고 있는지 테스트

namespace ServerCore
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[,] arr = new int[10000, 10000];
            {
                long now = DateTime.Now.Ticks; 
                for(int y =0; y<10000; y++)
                    for(int x=0; x<10000; x++)
                       arr[y, x] = 1;
                long end = DateTime.Now.Ticks;
                Console.WriteLine($"(y,x) 순서 걸린 시가 {end - now}"); 
            }

            {
                long now = DateTime.Now.Ticks;
                for (int y = 0; y < 10000; y++)
                    for (int x = 0; x < 10000; x++)
                        arr[x, y] = 1;
                long end = DateTime.Now.Ticks;
                Console.WriteLine($"(x,y) 순서 걸린 시가 {end - now}");
            }
        }
    }
}

상식적으로 생각하면 둘 다 시간이 같게 걸려야 한다 생각이 들어

ctrl+f5로 실행해 보면

걸리는 시간이 차이가 난다.

도대체 왜 시간이 차이가 나는지 이상할 수 있다.

캐시에 대한 내용 보면 **공간적 지역성(spacial locality)**이 있었어. 어떤 주소에 접근하면 인접한 주소는 접근할 확률이 높을 것이라다고 캐싱을 한다는 걸 알 수 있었어. 실제로 arr을 그림으로 보면

5 *5 배열이라 하면

[][][][][] [][][][][] [][][][][] [][][][][] [][][][][]

이런 식으로 x를 먼저 증가 시키면 순차적으로 앞으로 간다. 다음 애 접근할 때 캐시에 있으니까 굳이 메모리에 가서 가져오는게 아니라 상대적으로 빨라.

두번째는 x가 첫번째 값이면 띄엄띄엄 접근한다. 그러면 공간적 이점을 살릴수가 없다.

멀티스레드가 아니라도 캐시가 작동하고 있다는 걸 알아봤어.

멀티스레드로 가면 더 끔찍한 일들이 일어난다.

 

반응형

댓글