2.1 문자열을 키로 쓰는 map, set 성능 향상시키기
Programming/STL / 2011. 2. 7. 02:30
-
문자열(std::string)을 키로 가지는 맵 같은 경우, 문자열 비교 자체에 걸리는 시간 때문에 검색이 느려질 수 있다. 이 경우, 키로 사용하는 문자열이 별로 중요한 내용이 아니라면 아래와 같은 클래스를 사용함으로서 성능을 약간 증가시킬 수 있다.
//////////////////////////////////////////////////////////////////////////////// /// \class cStringKey /// \brief STL 컨테이너를 위한 문자열 키 //////////////////////////////////////////////////////////////////////////////// class cStringKey { private: enum { BYTE_SIZE = 32, }; char m_Text[BYTE_SIZE]; ///< 문자열 public: /// \brief 생성자 cStringKey() { memset(m_Text, 0, sizeof(m_Text)); } /// \brief 생성자 cStringKey(const char* text) { memset(m_Text, 0, sizeof(m_Text)); memcpy_s(m_Text, sizeof(m_Text), text, std::min(sizeof(m_Text), strlen(text))); } /// \brief 생성자 cStringKey(const std::string& text) { memset(m_Text, 0, sizeof(m_Text)); memcpy_s(m_Text, sizeof(m_Text), text.c_str(), std::min(sizeof(m_Text), text.size())); } /// \brief 복사 생성자 cStringKey(const cStringKey& rhs) { memcpy_s(m_Text, sizeof(m_Text), rhs.m_Text, sizeof(m_Text)); } public: /// \brief 대입 연산자 inline const cStringKey& operator = (const cStringKey& rhs) { if (this != &rhs) memcpy_s(m_Text, sizeof(m_Text), rhs.m_Text, sizeof(m_Text)); return *this; } /// \brief 비교 연산자 /// /// 이 함수는 약간 유의해야 하는데, 속도를 위해 루프를 풀어버렸기 때문이다. /// 클래스의 크기가 변경되면, 이 함수도 같이 변경해줘야 한다. inline bool operator < (const cStringKey& rhs) const { const int* buf1 = reinterpret_cast<const int*>(this); const int* buf2 = reinterpret_cast<const int*>(&rhs); if (*buf1 != *buf2) return *buf1 < *buf2; // 0-3 ++buf1; ++buf2; if (*buf1 != *buf2) return *buf1 < *buf2; // 4-7 ++buf1; ++buf2; if (*buf1 != *buf2) return *buf1 < *buf2; // 8-11 ++buf1; ++buf2; if (*buf1 != *buf2) return *buf1 < *buf2; // 12-15 ++buf1; ++buf2; if (*buf1 != *buf2) return *buf1 < *buf2; // 16-19 ++buf1; ++buf2; if (*buf1 != *buf2) return *buf1 < *buf2; // 20-23 ++buf1; ++buf2; if (*buf1 != *buf2) return *buf1 < *buf2; // 24-27 ++buf1; ++buf2; return *buf1 < *buf2; // 28-31 } };
대소문자 구별없이 비교를 할 수 없다는 점이 좀 아쉽다. 어셈블리도 좀 안다면 비교 연산자를 좀 더 깔끔하게 만들 수 있을 텐데. 어쨌든 테스트해보니, 릴리즈 빌드에서 약 25~33% 정도의 성능이 향상되었다.
typedef std::map<std::string, std::string> OLD_MAP; typedef std::map<cStringKey, std::string> NEW_MAP; OLD_MAP oldMap; NEW_MAP newMap; for (int i=0; i<1000; ++i) { std::string key = generic::to_string(rand() % 1000, 4); std::string value = generic::to_string(rand() % 1000, 4); oldMap.insert(OLD_MAP::value_type(key, value)); newMap.insert(NEW_MAP::value_type(key, value)); } DWORD begin = 0, oldTime = 0, newTime = 0; int repetition = 200000; begin = timeGetTime(); for (int i=0; i<repetition; ++i) { oldMap.find(generic::to_string(rand() % 1000, 4)); } oldTime = timeGetTime() - begin; begin = timeGetTime(); for (int i=0; i<repetition; ++i) { newMap.find(generic::to_string(rand() % 1000, 4)); } newTime = timeGetTime() - begin; std::cout << "OLD: " << oldTime << std::endl; std::cout << "NEW: " << newTime << std::endl;
반응형
'Programming > STL' 카테고리의 다른 글
STL list를 이용한 LIFO/FIFO 구현 (0) | 2011.05.24 |
---|---|
유틸 클래스 pair (0) | 2011.04.08 |
[펌] Effective STL 읽은 후 정리~ (0) | 2011.01.05 |
항목 27 : const_iterator를 iterator로 바꾸는 데에는 distance와 advance를 사용하자. (0) | 2010.11.03 |
부스트(boost) 관련 스프링노트 (0) | 2010.06.11 |