본문 바로가기
Computer Science Foundation/Operating System

[Memory] 부담없는 정리, 메모리

by 담백로봇 2024. 4. 21.

출처: https://modoocode.com/271


블로그는 부담없이써야 꾸준히 쓸 수 있는듯! 눈보단 손이 움직이면 더 습득률이 높을것이다.!  컴퓨터 메모리에 관한 좋은 정보라 조각 정리.

CPU 와 컴퓨터 메모리인 RAM 은 물리적으로 떨어져 있습니다. 따라서 CPU 가 메모리에서 데이터를 읽어 오기 위해서는 꽤 많은 시간이 걸립니다. 실제로, 인텔의 i7-6700 CPU 의 경우 최소 42 사이클 정도 걸린다고 보시면 됩니다. CPU 에서 덧셈 한 번을 1 사이클에 끝낼 수 있는데, 메모리에서 데이터 오는 것을 기다리느라, 42 번 덧셈을 연산할 시간을 놓치게 되는 것

 

캐시 ?

 

따라서 CPU 개발자들은, 이를 보완하기 위해 캐시(Cache) 라는 것을 도입. 캐시란, CPU 칩 안에 있는 조그마한 메모리.  램과는 다르게 CPU 에서 연산을 수행하는 부분이랑 거의 붙어 있다 싶이 해서, 읽기 / 쓰기 속도가 매우 빠르다는 점

L1, L2, L3 라 표시된 것이 모두 캐시 . CPU 에서 연산 하는 부분 (Core) 보다 캐시가 더 큼

 

CPU 에서 가장 많이 접근하는 메모리 영역은 L1 캐시에 가져다 놓게 되고, 그 다음으로, 자주 접근하는 부분은 L2, 마지막으로 L3 캐시 순으로 놓게 된다는 것.

CPU 가 특정한 주소에 있는 데이터에 접근하려 한다면, 1) 일단 캐시에 있는지 확인한 후, 캐시에 있다면 해당 값을 읽고, 2) 없다면 메모리 까지 갔다 오는 방식으로 진행됩니다. 이렇게 캐시에 있는 데이터를 다시 요청해서 시간을 절약하는 것을 Cache hit
이라과 하며 반대로 캐시에 요청한 데이터가 없어서 메모리 까지 갔다 오는 것을 Cache miss 라고 부름

 

CPU 가 어떻게 어느 영역의 메모리에 자주 접근할 지 어떻게 아는지? => 모름
 보통 CPU 에서 캐시가 작동하는 방식은
1. 메모리를 읽으면 일단 캐시에 저장해놓는다.
2.만일 캐시가 다 찼다면 특정한 방식에 따라 처리한다. *특정한 방식은 CPU 마다 다른데, 대표적인 예로가장 이전에 쓴(LRU - Least Recently Used)캐시를 날려버리고 그 자리에 새로운 캐시를 기록하는 방식

 

 

CPU 파이프라이닝 (pipelining) 

동시에 여러 개의 작업을 동시에 실행하는 것을 파이프라이닝(pipelining) 이라고함.

 

예제로 빨래하는 예제

 

위와 같이 모든 단계의 작업들을 쉬지 않고 계속 돌릴 수 있습니다. 즉, 이전의 방식은 효율이 33% 였다면, 새로운 방식의 경우 모든 단계를 100% 사용할 수 있게 되지요. 이와 같이, 한 작업 (세탁 - 건조 - 개기) 이 끝나기 전에, 다음 작업을 시작하는 방식으로 동시에 여러 개의 작업을 동시에 실행하는 것을 파이프라이닝(pipelining) 이라고 합니다.

 

CPU 도 마찬가지입니다. 실제 CPU 에서 명령어를 실행할 때 여러 단계를 거치게 됩니다. 명령어를 읽어야 하고 (fetch), 읽은 명령어가 무엇 인지 해석해야 하고 (decode), 해석된 명령어를 실행하고 (execute), 마지막으로 결과를 써야 하지요 (write). CPU 역시 정확히 동일한 방법으로 명령어를 처리합니다. 아래에는 CPU 파이프라이닝 예제!!

실제로는 실행 부분의 실행 속도는 명령어마다 천차 만별입니다. 따라서, 만일 매우 실행 시간이 오래 걸리는 명령어가 있다면, 해당 작업 때문에 다른 명령어들이 쫙 밀리게 되겠지요. 따라서, 컴파일러는 우리가 어떠한 최대한 CPU 의 파이프라인을 효율적으로 활용할 수 있도록 명령어를 재배치하게 됩니다. 따라서 우리의 foo 함수 처럼, 멀티 쓰레드 환경에서는 예상치 못한 결과가 나올 수 도 있음

 

= > CPU안 작업 실행 속도가 천차만별이기때문에 컴파일러는 알아서 효율적인 명령어를 배치하기 때문에 sequence적인 결과가 나오지 않을 수 있다는 말

 

무엇을 믿나?
 이렇게 명령어 순서도 뒤죽 박죽 바꾸고 심지어 CPU 에서도 명령어들을 제대로 된 순서로 실행하지 않는다면, 도대체 무엇을 믿을 수 있을까?

C++ 의 모든 객체들은 수정 순서(modification order) 라는 것을 정의할 수 있습니다. 수정 순서라 하는 것은, 만약에 어떤 객체의 값을 실시간으로 확인할 수 있는 전지전능한 무언가가 있다고 하였을 때, 해당 객체의 값의 변화를 기록한 것이라 보면 됩

T는 쓰레드

어떤 쓰레드가 a 의 값을 읽었을 때, 8 로 읽었다면, 그 다음에 읽어지는 a 의 값은 반드시 8, 6, 3 중에 하나여야 할 것입니다. 수정 순서를 거꾸로 거슬러 올라가서 5 를 읽는 일은 없습니다. 모든 쓰레드에서 변수의 수정 순서에 동의만 한다면 문제될 것이 없습니다. 같은 시간에 변수 a의 값을 관찰했다고 해서 굳이 모든 쓰레드들이 동일한 값을 관찰할 필요는 없다 라는 점!!

 

그럼 각 스레드에서 값을 읽는 시점에 동일한값을 얻기위해서는? 동기화 작업을 하면되지만 꽤나 시간을 잡아먹는일... => 이를 원자성 (Atomicity)로 해결한다. c++이기에 로우하게 접근해 해결한다.

 

대부분 퍼와서 편집한 포스트이지만 이분은 정말 설명을 잘한다. 우선 억지로 친해지기

댓글