본문 바로가기
Cache, Cash?

Cache라는 건 뭡니까? Cash가 드는 넘 이죠. 흐흐. Cache라는 건 교활한 놈이에요. 돈 잡아 먹는 기계죠. 왜냐! 느린 Memory와 빠른 CPU사이에 장난을 쳐서 마치 빠른 것처럼 만들어 주는 사기꾼인 거죠. 흔히들 System의 성능을 향상시키는데 Cache가 아주 중요한 역할을 한다고 말하는 데 도대체 무엇 때문에 중요하다고 하는지. 원래는 CPU와 Memory사이는 사실 속도가 느리죠. 쓰고 읽기가 느린 거에요. Wait State에서도 보았겠지만, CPU와 Memory의 처리 속도는 아~주 천차만별인 게죠. 엄청 빠른 형님과, 느림보 동생이 2인 삼각을 하려면 빠른 형님이 계속 기다려줘야 하는 이치인 게죠. 쩝. 그러다 보니까, 아무리 빠른 CPU를 쓰더라도 느린 Memory를 사용하게 되면 System이 전반적으로 느릴 수 밖에 없는 문제점을 드러내죠.
 
하지만, 역시나 하늘은 스스로 돕는 자를 돕는 법이라고. Software의 특성을 자세히 보면 비스므리한 특성들이 엄청 있더라는 거죠. 그것이 무엇이냐.
 
1. Temporal Locality
    이게 먼소리냐 하면, Software가 한번 어떤 Memory 상의 주소를 Access하게 되면 자주 접근하게 되더라 라는 특성이 있구요.
 
2. Spatial Locality
    이건 또 먼소리냐 하면 Temporal Locality와 비스므리한 Concept인데, 1번과 거꾸로의 말인데 어떤 영역을 접근하려고 보면 이미 접근했었던 영역일 가능성이 크거나, 이미 접근이 이루어진 Memory상의 주소의 근처일 가능성이 크다 라는 특성이에요.
 
어려운 말 한번 찾아서 써봤습니다.
 
오호, 이 두 가지 특성을 이용한다면 한번 접근 했던 Memory 영역은 더~빠르고 더~ 비싼 Memory에다가 잠시 넣어두고 있으면 금새 또 접근할 일이 생기니까 굳이 느린 Memory에서 꺼내올 필요 없이 그냥 더~빠르고 더~비싼 Memory에서 꺼내 쓰면 되겠구나! 하는 것이 Cache의 기본 Idea라지요. 이 더~빠르고 더~비싼 Memory는 TCM이라고 해서, Wait State가 0 인 Memory가 있는데 이 녀석을 많이 사용하고 있어요.

자, 이런 Concept으로 Cache가 탄생했으니 이렇게 생겨 먹었겠죠. 이럴 때 Cache에 관련된 용어 두 가지 Hit와 Miss를 알아둬야 하겠사옵니다. Hit는 오! 내가 Access하려는 주소가 Cache에 저장되어 있구나, 라는 거고, Miss는 오! 내가 미쓰김을 만났구나….는 아니고 아차! Cache에 없으니 Memory에 직접 Access해서 가져와야겠구나. 는 의미 에요. 일단 Hit가 났으면 오케바리 그냥 가져오면 되겠지만 Miss가 나면 Memory에서 가져 와야 하는데 어떻게 가져 오느냐! Spatial Locality를 잊지 않았겠지요. 큰 단위로 가져오는 거에요. 그 단위를 Block이라고 부르는데 그 단위로 가져오면서 Cache Memory에 저장시켜 놓는 거죠.

그러면 여기서 질문하나, Cache Memory의 크기가 무한정 있지 않은데 이런 식으로 계속 불러오면, Cache Memory가 모자를 때 원래 Cache Memory에 들어 있던 녀석들은 어떻게 할 꺼냐… 하는 질문이 생기겠죠. 그거야 말로 Cache Memory에 있던 녀석들을 잘 버려야 되는데 Locality에 의해서 지금 가져온 녀석은 계속 쓸 일이 생기겠지만, Cache에 오래 버티고 있었던 녀석은 아무래도 다시 쓸 일이 적겠죠. Cache Memory에 있던 내용을 replace하게 되는 방법으로 오래 버티고 있었던 녀석 자리에 새로운 녀석으로 갈아치우는 거죠. 이걸 LRU Policy라고 부르고요. System 마다 여러 가지 방법을 동원해서 Cache의 성능을 극대화 해주는 거에요.
 
지금 계속 Cache와 Cache Memory로 구분해서 애기하고 있었다는 걸 느꼈는지 모르겠지만, Cache = Cache Controller + Cache Memory로 구성되어 있고요. Cache Controller라는게 hit/ miss/ LRU 뭐 이런 걸 담당하고 있는 녀석이고, Cache Memory가 그런 Memory의 내용을 담고 있는 Memory라고 보셔야 해요. 그래서 Cache Controller를 Cache라고 부르고, 그것에 딸려 있는 Memory를 Cache Memory라고 부르고 있답니다. 이 부분에서 Cache의 원리에 대해서 잘 설명해 봐야겠다고 생각했지만, 이런 것까지 알아야 정말 멋진 Engineer가 되는 것인가 라는 질문에 에이~ 설마~ 라는 답이 나온 관계로, Cache의 정확한 Hardware적인 구현 원리는 다루지 않고요, 아주 간단한 원리와 다른 여러 가지 Cache Control 관련한 얘기로 채워 보려 해요.
 
Cache중에 뭐~ Way Associative Cache라는 거 나오죠. 뭐~ 별거 아니에요. 예를 들어서 16KB Cache Memory가 있다고 쳐봐요. 그러면 2 Way 라는 건 8KB씩 두 개의 Block을 만들어 놓고 쓴다는 얘기고요, 4Way 라는 건 4KB 씩 4개 Block 만들어 놓고 쓴다는 얘기고요, 16Way라는 건 1KB씩 Block 만들어 놓고 Cache를 사용한다는 말이죠. 이런 Block을 page라고도 불러요. 별거 아니에요. 이런 거 하기 위해서 뭐 offset이니 Index니 Tag니 하는 말 쓰는데 이런 Cache를 나눠 쓰기 위한 용어들이에요.
 
자, 이제까지는 읽어 들이는 얘기만 했으니, 쓰는 얘기도 해봐야죠. 사실 Cache만 있는 System도 있는데, Cache에 Write Buffer라는 걸 두어서 쓰기 성능을 좋게 한 System도 있어요.

이런 식인데요, Write Buffer를 이용해서 CPU가 좀 더 효율적으로 다른 일을 할 수 있도록 해주는 System이에요.

요 때, Write Through와 Write Back이라는 용어가 난무하는데요, Write Through라는 용어는 쓰루 패스와 마찬가지로 Memory에 뭔가를 쓸 때 그 값이 Cache위에 올라와 있는 값일 경우에 곤란하겠죠. Cache와 Memory값이 다르면 안되니까요. Write Through를 쓰게 되면 Cache와 Memory에 모두 Update를 같이 해버리는 거지요. 간편한 System이죠. Cache고 나발이고 별로 신경 안 써도 되고요. 그러면 Write Back이라는 건 뭘까요? Data를 쓸 때 Memory에는 안 쓰고요 Cache만 update하는 방법이에요.

오호라, Write Through보다 훨씬 빠르겠죠. 나중에 Cache에 있는 내용을 버리게 되는 경우에 update를 해주는 거죠. 하지만 단점이 있는 게, Write Back을 하면 속도가 빠른 대신에 Memory와 Cache가 서로 값이 다르게 된다는 단점이 있어요. 이런 걸 Inconsistency라고 부르는데요. 요렇게 되면 다른 건 그렇다 치더라도 Device를 Control할 때 Write back을 쓰게 되면 실제 Device에 값을 써야 되는데 Cache에만 써놓고, Device에 값을 안 넘기는 경우가 생겨요. 예를 들면 LCD 같은 데에다가 Data를 뿌리고 싶은데 LCD에다가 값을 썼더니 Cache에만 값을 쓰고 LCD에다가는 값을 안 넘겨줘서 LCD에 더 이상 그림이 안 뿌려지는 경우도 발생하는 거죠. LCD에 열라게 update를 해줘도.. ㅋ.

여기에서 다시 한번 Write Buffer의 역할을 얘기하자면 쓰기 할때 FIFO buffer를 가지고 CPU대신에 Memory에 Data를 적어주는 일을 하는 거에요. Write Through할 때도 그렇고 Write Back할 때도 그렇고. Cache자체를 사용 자제해야 하는 경우도 있어요. 그런 경우가 뭐냐 하면 DMA랑 같이 사용할 때죠. CPU는 DMA를 통해서 뭔가를 대량으로 썼을 경우에 실제 Memory는 update되었습니다만, Cache는 Update가 안되었을 수가 있지요. 이런 경우에도 실제 Memory와 Cache가 Inconsistency한 상황이 발생하게 되는 거에요. 참내... 자, 이런 경우에는 교활한 Cache는 독이 될 것이냐. 하면 다 방법이 있어요.

Cache Flush나, Cache Clean을 이용하거나, MMU의 Page Table을 Cacheable - Non Cacheable 영역으로 정해 버리는 거에요. 음 어렵다. MMU의 Page Table 얘기는 바로 다음에 나오니 다음으로 미루고요, Cache Flush나 Cache Clean을 다뤄봐야겠네요. Cache Flush는 Cache Invalidate라는 말로도 많이 불리는데요, Flush하면 뭔가 화장실에서 물을 콰~ 내리듯이 Cache 안의 내용을 콰~ 내린다고 봐야 하겠죠. 말 그대로 콰~ 내리는 거에요. 마치 Reset하듯이. Flush를 하는 이유에는 여러 가지가 있겠지만, 어느 순간에 Cache와 Memory가 서로 Inconsistency가 의심되는 순간이 되면 콰~ 물을 내려버리는 거에요. 이제 와서 말이지만, Cache는 Instruction용과 Data용을 따로 사용하는 경우도 있거든요. 이런 경우에는 둘 중에 하나를 선택하고 콰~ 물을 내려줄 수 있어요. 이제부터는 처음부터 다시 시작하는겨~ 하면서 콰~ 물을 내리는 거에요. Cache Clean은 Data Cache에 해당하는 말인데요, Clean을 하게 되면 Cache에 있던 내용을 Memory에도 update해 줘요.
 
요때! 또 하나 알아둬야 하는 용어가 있는데, Dirty라는 용어인데요. 요 Dirty라는 게 Cache에만 update되고 Memory에는 update안되어 있는 경우에 더럽혀 졌다는 의미로 Dirty라는 용어를 써요. Write Back으로 사용하면 그렇겠죠. ARM9의 경우에는 아래와 같이 Cache Flush 함수를 구현할 수 있는데,
 
 
cache_flush
   ldr     r1, =0x0
   mcr     p15, 0, r1, c7, c7, 0
 
 
요기 보면 좀 이상해 보이는 거 있지 않나요? 오, Cache도 Coprocessor 15번을 이용해서 뭔가 하네요. Coprocessor 15은 MMU도 Control 하는데 뭔가 연관성 있어 보이지 않아요? 그래요. MMU를 이용하면서 Page Table에다가 Cache의 속성도 줄 수가 있는 거에요. MMU 얘기를 아직 안 하니까 좀 곤란하죠. 바로! 다음에 연결되니까! 같이 가보시죠.

 

이전에 언급되었던 Cache 갈아치우기 Policy도 CP15의 C1[14] bit를 통해서 Round Robin 또는 Random으로 설정할 수 있어요.

여기에서도 볼 수 있듯이, Cache와 MMU와 CP15는 서로 뗄래야 뗄 수 없는 관계에요. 얽히고 섥혀 있죠. Cache의 성능을 높이는 방법 중 하나로서는 Locality를 높이는 거에요. 자주 같이 쓰이는 것들은 Cache 크기에 맞추어 Memory에 Module별로 몰아 놓던가 하는 수법이죠. 또는 자주 쓰이는 전역변수 같은 거 모아 놓는 것도 그 한가지 예가 될 수 있겠네요. 하여간 뭔가 성능 높이려고 하면 이렇게 복잡한 걸 만들어내 야하고, Engineer가 수고로움을 마다하지 않아야 하는 이런 현실이 원망스럽긴 하지만 조금만 참아주세요!

Commented by highseek at 2009/11/15 16:44
오래전에 배운 거라 좀 가물가물한데.. 깔끔하게 정리하셨군요.

잘 보고 갑니다 :)
Commented by 히언 at 2009/11/15 23:26
아하하~ 괜찮았나요~?
아무쪼록 감사합니다~
Commented by 흑곰 at 2009/11/15 21:01
좋은 내용 감사합니다 +_+)>;
Commented by 히언 at 2009/11/15 23:27
에헤헤~ 이쁘게 봐주시니,
어쩐대요~ 너무 좋아요~ 어흥~
Commented by 레몬향최루탄 at 2009/11/16 10:07
오우 좋은내용이네요 잘보고 가효-
Commented by 히언 at 2009/11/16 20:24
이히힣.

그냥 잘보고 가시면 안되요~ 또 오세요~
Commented by 마늘빵 at 2009/12/11 12:46
오우... 어려운 개념 정말 알기 쉽게 써주셨네요.
자주 와서 봐야겠어요 ㅎㅎ
Commented by kyle at 2009/12/12 10:21

음 ... 제가 사용해본 CPU ( PPC/ ARM ) 등에서는 좀 다른거 같아서요.

Cache Flush 와 Cache Invalidate 는 어찌보면 반대의 개념 입니다.
write back 을 사용하는경우 cache의 내용을 main mem에 반영하는것이 Flush 이고
invalidate의 경우는 해당 cache line을 dirty 하다고 표시한는것 입니다.

예를 들어 DMA unit이 main mem을 직접 access하는경우 main mem은 cache 모르게
change 됩니다. 그런경우 CPU는 해당 mem을 cache 에서 직접 읽는것을 피하기 위해서 invalidate를 통해서
dirty mark를 하게 됩니다.

다시보면 Flush = cache -> main mem 이고 Invalidate = main mem -> cache 입니다.
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.
친절한 임베디드 시스템 개발자 되기 강좌 글 전체 리스트 (링크) -



댓글





친절한 임베디드 개발자 되기 강좌 글 전체 리스트 (링크) -