Posts [2018 KAKAO BLIND RECRUITMENT] [1차] 캐시
Post
Cancel

[2018 KAKAO BLIND RECRUITMENT] [1차] 캐시


[2018 KAKAO BLIND RECRUITMENT] 캐시

2018 KAKAO BLIND RECRUITMENT 캐시 문제입니다.

https://programmers.co.kr/learn/courses/30/lessons/17680


문제

지도개발팀에서 근무하는 제이지는 지도에서 도시 이름을 검색하면 해당 도시와 관련된 맛집 게시물들을 데이터베이스에서 읽어 보여주는 서비스를 개발하고 있다. 이 프로그램의 테스팅 업무를 담당하고 있는 어피치는 서비스를 오픈하기 전 각 로직에 대한 성능 측정을 수행하였는데, 제이지가 작성한 부분 중 데이터베이스에서 게시물을 가져오는 부분의 실행시간이 너무 오래 걸린다는 것을 알게 되었다. 어피치는 제이지에게 해당 로직을 개선하라고 닦달하기 시작하였고, 제이지는 DB 캐시를 적용하여 성능 개선을 시도하고 있지만 캐시 크기를 얼마로 해야 효율적인지 몰라 난감한 상황이다.

어피치에게 시달리는 제이지를 도와, DB 캐시를 적용할 때 캐시 크기에 따른 실행시간 측정 프로그램을 작성하시오.

  • 입력 형식, 출력 형식
    • 캐시 크기(cacheSize)와 도시이름 배열(cities)을 입력받는다.
    • cacheSize는 정수이며, 범위는 0 ≦ cacheSize ≦ 30 이다.
    • cities는 도시 이름으로 이뤄진 문자열 배열로, 최대 도시 수는 100,000개이다.
    • 각 도시 이름은 공백, 숫자, 특수문자 등이 없는 영문자로 구성되며, 대소문자 구분을 하지 않는다. 도시 이름은 최대 20자로 이루어져 있다.
    • 입력된 도시이름 배열을 순서대로 처리할 때, 총 실행시간을 출력한다.
  • 조건
    • 캐시 교체 알고리즘은 LRU(Least Recently Used)를 사용한다.
    • cache hit일 경우 실행시간은 1이다.
    • cache miss일 경우 실행시간은 5이다.

생각한 아이디어

먼저 캐시 교체 policy 중 LRU 에 대해 정확하게 알 필요가 있습니다.





LRU 알고리즘을 간단한 과정으로 표현해 본 것입니다.

즉, 각 엔트리마다 고유한 숫자값이 캐시에 처음 들어올 때 0부터 시작합니다.

그리고 나 외에 다른 값들이 들어오거나 액세스 될 경우 자신의 숫자값, 즉 순위를 증가시킵니다.

숫자값이 가장 큰 것을 “가장 최근에 사용하지 않은 엔트리” 로 판단하고 교체 대상(victim)이 되도록 하는 것입니다.

기본적으로는 이 값이 캐시에 들어온 순서를 의미하겠지만,

Cache Hit가 되었을 때는 그 엔트리가 가장 최근에 사용된 것이 되므로, 그에 맞게 순위값을 조정해 주어야 합니다. 위 그림들에서 마지막 그림일 때를 말합니다.


소스코드

먼저 문제 조건에서 대소문자를 구분하지 않고 같게 취급한다고 하였으므로, 인풋으로 들어올 도시 이름들을 일괄적으로 전부 대문자로 바꾸거나, 소문자로 바꾸는 작업을 하였습니다. (C++의 string 관련 tolower, toupper 함수)

그 다음 캐시를 map 형태로 정의해 보았습니다. 이유는, LRU를 정하기 위한 순위값을 각 도시 이름과 함께 저장하기 위해서입니다.

예를 들면, 처음 “LA” 라는 도시가 캐시에 들어갈 경우 LRU_Cache[LA] = 0 입니다.

그 다음 다른 도시들이 들어오거나 할 경우 LRU_Cache[LA] 의 value 값을 1 증가시키면 됩니다.

이런 식으로 그림처럼 캐시에 들어가 있는 각 도시(엔트리) 마다의 순위값을 조정하는 상황을 표현하였습니다.

그리고 Cache Miss, Cache Hit인 상황을 구분하여 차례대로 코드에 담아 보았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#define cache_hit 1
#define cache_miss 5

#include <string>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;
map<string, int> LRU_Cache;

int solution(int cacheSize, vector<string> cities) {
    int exe_time = 0;  
	map<string, int>::iterator iter;
	map<string, int>::iterator erase_iter;
    
    // 대소문자 구분을 안하므로, 모두 소문자로 변경해 놓는다.
	for (int i = 0; i < cities.size(); i++) {
		for (int j = 0; j < cities[i].size(); j++) {
			cities[i][j] = tolower(cities[i][j]);
		}
	}
    
    for (int i = 0; i < cities.size(); i++) {

		iter = LRU_Cache.find(cities[i]); // 캐시에 있는지 없는지 판단

		// Cache size가 0, 즉 캐시가 없을 경우는 특이 Case로 100% miss
		if (cacheSize == 0) exe_time += cache_miss;

		// (1) LRU_Cache에 해당 요소가 존재하지 않을 경우
		else if (iter == LRU_Cache.end()) {
			// miss++
			exe_time += cache_miss;

			// map size가 정해진 cacheSize 보다 작을 경우,
            // 캐시 내에 빈 공간이 있다는 의미이므로,
            // 캐시 내부 모든 요소의 순위 값을 1씩 증가시키고
			// 새로 들어갈 것을 insert 한다.
			if (LRU_Cache.size() < cacheSize) {
				for (iter = LRU_Cache.begin(); iter != LRU_Cache.end(); iter++) {
					iter->second += 1;
				}
				LRU_Cache.insert({ cities[i], 0 });
			}

			// 캐시가 꽉 찼을 경우, 순위 값이 가장 큰 것을 지우고,
            // 캐시 내부 모든 요소의 순위 값을 1씩 증가시킨 다음,
            // 새로 들어갈 것을 insert 한다.
			else {
				int max_value = -1;
				for (iter = LRU_Cache.begin(); iter != LRU_Cache.end(); iter++) {
					if (max_value < iter->second) {
						max_value = iter->second;
						erase_iter = iter;
					}
				}
				LRU_Cache.erase(erase_iter);
				for (iter = LRU_Cache.begin(); iter != LRU_Cache.end(); iter++) {
					iter->second += 1;
				}
				LRU_Cache.insert({ cities[i], 0 });
			}
		}	

		// (2) LRU_Cache에 해당 요소가 존재할 경우
		else {
			// hit++
			exe_time += cache_hit;

			// 찾은 요소를 가장 최신으로 만들어야 한다.
			iter->second = 0;

			for (iter = LRU_Cache.begin(); iter != LRU_Cache.end(); iter++) {
				// 다른 모든 요소의 LRU 값을 1 증가시킨다.
				if (iter->first != cities[i]){
					iter->second += 1;
				}
			}
		}
	}
    
    return exe_time;
}

출처: 프로그래머스 코딩 테스트 연습, https://programmers.co.kr/learn/challenges

본 포스팅은 광고를 게재하지 않습니다.