C++ STL의 set이 성능이 떨어지나요?
2012.03.26 23:30
C와 C++로 작성한 동일한 코드를 gcc, g++로 각각 컴파일할 때, 성능차이가 약 3배정도 나는 것은 알고 있습니다.
게다가 Visual Studio의 컴파일러로 컴파일하면, 훠얼씬 느린 것도 알고 있습니다.
(VS로 8분 걸리는게, g++로 20초 걸린것도 있습니다.)
stl로 복잡한 프로그램을 안만들어보다가, 이번에 새로 만들고 있는데..
알고리즘 자체가 time complixity가 상당히 높은거라서 성능은 각오하고 만들고 있는데
좀 심각하게 느린 것 같습니다.
1000개를 self-join해야하는데 stl set을 쓰니 아주 느리네요.
1000개를 self-join하면 1000 * 1000 - 1000개, 뭐 대충 100만개가 나오긴 하는데
이정도는 C++로 생으로 짜면 수분 내에 연산될만한 수준 아닌가요?
stl set으로 짜니 거의 수십분이 걸립니다.
컴파일러는 visual studio 2010이고요.
직접 구현하려다가 너무 귀찮아서(역시 귀차니즘이 문제) set을 쓴건데
원래 성능이 느린건가요? 아니면 제가 현대 컴퓨터의 연산능력을 너무 과신하는건가요?
아.. 간과한게 있는데 제 코드가 엉망일수도 있겠네요..ㅠㅠ
사실 이 가능성이 젤 높겠군요..
코멘트 15
-
클라우드나인
03.27 00:02
자세한 답변 감사드립니다.
현재 set을 쓰는 부분은 다음과 같이 구성되어 있습니다.
struct Itemset {
std::set<int> item
}
std::vector<Itemset> itemsets
for itemsets.begin; to end; iter1++
for itemsets.begin; to end; iter2++
If (iter1의 마지막 원소를 제외한 부분집합 == iter2의 마지막 원소를 제외한 부분집합)
insert to newItemsets
간단한 구조인데, 1000개 사이즈로 100만번 계산하는 과정이 거의 1시간이 걸립니다.
아얘 엎고 map인지 하는 놈으로 갈아버릴까 싶습니다. 벡터와 셋을 같이 썼더니 성능이 엉망이 된 것 같습니다. -
클라우드나인
03.27 00:04
모바일로 썼더니 인덴테이션이 날아갔네요. 양해 부탁드립니다.
가영아빠님 댓글을 읽고 코드를 다시 보니, 느릴 수 밖에 없는 코드네요. Stl의 성능튜닝부분을 못쓰도록 만들었으니..
맵을 보고 그게 적절하다면 갈아엎어야겠습니다. 자세한 답변 감사드립니다. -
클라우드나인
03.27 00:21
음. 그냥 g++로 컴파일하니 수분 내에 끝나네요.
역시......... Vs컴파일러는 ㅡㅡ 별로네요.
한시간짜리가 분단위로 끝날줄이야. -
앗, 벌써 해결 보셨군요. 일단 VS계열이 사용하는 std stl보다는 stlport를 써보세요.
그리고 의사코드 상으로는 특별히 느려질만한 부분으로는 제외한 부분집합이라는 부분 뿐인데요.
vs std stl과 stlport가 ptr을 다루는 부분부터 많이 달라서요. vs std stl은 조심해서 쓰지 않으면 많은 패널티가 있습니다.
심지어 find 처럼 단순한 함수더라도 set은 어떤 자료형을 어떻게 쓰냐에 따라서 기복이 심하거든요 =_=
-
클라우드나인
03.27 00:47
예전부터 잊을만하면 된통 당하고 있는데, VS컴파일러는 성능이 정말 안좋네요.
이걸로 만든 프로그램이 전 세계에 엄청나게 깔려있다는건 충격입니다. 설마 오피스를 VS로 컴파일하진 않았겠죠?.. ㅜㅜ 아니, 윈도우와 그 시스템프로그램을 설마 VS로 컴파일하진 않았겠죠?....
1500%의 성능차이라니 이건 좀 심한 것 같습니다.....
주의주신 말씀은 잘 새겨듣고, 앞으로 조심해서 쓰겠습니다.
항상 자세히 알려주셔서 감사드립니다. ^^
-
Debug 모드로 컴파일하신거 아닌가요? VC의 STL이 느린편이긴 해도 저 정도는 아닌데 말이죠. 전세계 유수의 계산 프로그램들이 VC의 STL로 컴파일해서 사용중입니다. 나름대로 최적화 노하우가 필요하긴 해도 그렇게까지 느리진 않아요.
특히 VC의 STL을 Debug 모드로 컴파일하면 모든 Debug 옵션이 전부 다 켜지기 때문에 엄청나게 느려집니다. Release 모드에서는 #define _SECURE_SCL 0 을강제로 해주는게 좋습니다.
-
클라우드나인
03.27 01:33
컴파일러 옵션은 Release 모드에서 maximize speed와 maximum optimize 두 개의 프리셋을 적용해 보았으나, 여전히 오래 걸렸습니다. 그 외의 옵션은 손대지 않았습니다. 예전 기억엔, 오히려 손대면 더 느려지더라구요.
그리고 메모리를 좀 먹는 프로그램이라서 64-bit로 컴파일해 봤는데 눈에띄는 차이는 없었습니다.
아마도 제 코드가 VS와는 상극인가보네요.. ㅜ_ㅜ
#define _SECURE_SCL 0도 한번 적용해서 테스트 해 보겠습니다.
감사합니다. ^^
-
http://sweeper.egloos.com/2819872 참고하세요~ 저 글에서는 성능향상이 미미하다고 나와있는데, 제 경험상으로는 코드마다 많이 달랐습니다. 실제로 효과가 아주 큰 경우도 자주 보입니다.
-
클라우드나인
03.27 01:36
오오 +_+ 이것들은 처음 듣는 내용들이네요 ^^
위에 말씀해 주신 define문이 무슨 의미인지 검색해 보려던 참이었는데, 좋은 링크 감사드립니다. ^^
잘 읽어보고 어서 초보탈피해야겠습니당.. ㅜ.ㅜ
-
MS의 사상 자체가 "개발자를 믿지 않는다"로 바뀌게 되면서, 저런 옵션들이 가능한 타이트하게 나오질 않는 편이예요. 심지어 iterator array 체크조차 개발자들은 제대로 하지 않는다는거죠. 심지어 VC의 STL은 multithread에서도 가능한 쓸수 있게 하려고 노력한 흔적들이 보이는데, 이게 속도를 죽이는 역할을 많이 합니다. 물론 한계가 있어서 가능하면 multithread에서는 STL을 쓰지 않거나 lock을 걸어주어야 하긴 하죠.
그래서 MS는 ATL을 더 권장합니다. ATL은 MS의 모든 통제하에 처음부터 만들어져있고, iterator를 쓰지 않기 때문에 훨씬 안전합니다. 저 iterator라는게 참 좋은 개념이긴 한데, 동시에 두군데서 접근할수 있는 현재의 multi-core multi-thread 시스템에서는 완전히 에러입니다. 툭하면 죽고 툭하면 에러나고... 근데 왜 에러가 나는지 알 길도 없어요.
뭐... 그러는 저도 아직까지 ATL보다는 STL을 더 많이 사용합니다. 손에 익숙하고 호환성 하나는 좋거든요. :)
-
컴파일러라는게 참 애매한게, Intel 컴파일러가 참 좋기로 많이 알려져있는데 이게 꼭 빠른것만은 아니더라는겁니다. 사실 그거 얼마 하지도 않는데 왜 다들 안사서 쓰겠어요. 요즘은 그냥 설치하고 VS에서 메뉴에서 한번 뚝 눌러주기만 하면 자동으로 다 알아서 해주기까지 하거든요.
그런데 코드에 따라서 어떤 경우엔 빨라지고 어떤 경우엔 그대로고 어떤 경우엔 아예 느려지기까지 합니다. ;;; 게다가 소숫점 대빵 많이 들어간 연산의 경우 SSE를 사용하다가 round robin의 문제로 답이 달라지기까지 합니다.!!! 아주 곤란해지죠. 거기다 MultiThread로 개발을 하다보면 Thread간의 미묘한 조율때문에 단순히 한군데서 빨라진다고 성능이 향상된다고 볼수가 없어집니다. 보통 성능이 빨라진다는건 그 순간에 그 로직이 CPU를 더 많이 사용한다는걸 의미하거든요.
말씀하신것처럼 최적화 옵션이라고 해서 꼭 빨라지는게 아닌것도 같은 이유입니다. 결국 속도도 속도지만 그 컴파일러에 가장 맞는 코드를 만들었는가가 결국은 더 중요해집니다.
-
아.. 참고로 성능때문이시라면 map은 쓰지 마세요~ map은 검색을 위해 만들어진거라 요소가 추가/삭제될때마다 sorting을 해대거든요. 속도에선 쥐약입니다. hash_map을 쓰라고들 많이 이야기되는데, 그것도 별루이고 STL 표준도 아닙니다. :(
-
midday님이 얘기하신 #define _SECURE_SCL 0 은 vs2005이후 더블체크와 릴리즈모드 체크로 인한 문제로 많이들 쓰긴 하는데요.
사실 전 권하진 않습니다. 경험적으로 이게 퍼포먼스에서 큰 효과를 보는 경우가 드믑니다. 짧은 데이터로는 2~3배차이난다고 하지만 릴리즈모드에서는 평균 10% 안쪽이고 컨테이너와 아이템 종류와 양에 따라서는 0.0x% 정도 차이정도 안 보이기도 합니다. 한마디로 iterator를 어디서 어떻게 쓰는지 코딩습관만 고쳐도 차이가 확 날수도 미미할 수도 있다는 얘깁니다.(앞서 얘기한 set의 find를 조심하라고 한 부분은 자료형에도 문제가 있지만 예상하셨다 시피 iterator로 전체를 검사하기 때문에 SECURE_SCL 이 퍼포먼스를 갉아먹는 것으로도 대표적 예입니다. 하지만 이는 습관적으로 find를 안쓰면 차이가 미미해지죠.)
게다가 결정적으로 _SECURE_SCL 값에 따라서 외부라이브러리를 못 쓸 수도 있고 보안관련 옵션이 다 꺼져버립니다. 실제로 상용라입중 STL을 사용하면 _SECURE_SCL을 거의 켜놓고 제공합니다. exploit에 대한 모든 secure옵션을 다 꺼버리기 때문에 당연히 키고 제공을 하는데요.
이럴 경우 라입쪽에 무조건 맞춰야만 쓸 수 있고, 모듈별로 혼용이 불가능합니다. 게다가 크러쉬덤프에서도 문제가 있기도하고요.
문제는 사용자들은 퍼포먼스 때문에 _SECURE_SCL 을 끄기를 원하고, 실제로 언제부터인가 이게 유행이 되버렸습니다.
뭐 당연하지만 퍼포먼스에 목매는 것은 개발자의 첫번째 덕목이니까요. 문제는 보안이라서 이제는 퍼포먼스 vs 보안의 경중을 가려야 한다는 것인데요. 다 꺼버리면 exploit에 대처하는 방법이 전무하니 그거도 문제고요.
안그래도 STL 세미나에서 secure쪽에 대한 말들이 많아서 VS2010에서는 unchecked type 을 따로 제공합니다.
#ifndef UNCHECKED
#define ITERATOR std::list<int>::iterator
#define BEGIN_OF(p) p.begin()
#define END_OF(p) p.end()
#else
#define ITERATOR std::list<int>::iterator::_Unchecked_type
#define BEGIN_OF(p) p.begin()._Unchecked()
#define END_OF(p) p.end()._Unchecked()
#endifint _tmain(int argc, _TCHAR* argv[])
{
std::list<int> l;
l.push_back(10);
l.push_back(9);
l.push_back(8);
l.push_back(7);ITERATOR iter = BEGIN_OF(l);
while( iter != END_OF(l) )
{
printf( "%d\n" , *iter ) ;
iter++;
}return 0;
}간단한 샘플인데 어떻게 동작하시는지 금방 아실겁니다.
코드는 구리지만 이렇게 해서 퍼포먼스가 정말 중요한 부분만 빼시고, 나머지는 그대로 SECURE_SCL을 on으로 두시는게 좋습니다.
-
이거 함 써봐야겠네요~ 감사합니다~
STL의 성능은 1. 어떤 태플릿라이브러리를 쓰느냐. 2. 사용자가 어떻게 쓰느냐에 따라서달라집니다.
1.은 구현의 문제, 즉 알고리즘의 차이입니다. 보통set은 tree구조인데 어떤 트리냐 등등의 차이죠.
2. 사용자가 어떻게 쓰느냐인데, 이는 STL의 컨테이너는 Allocator를 포함하고 다분히 메모리를 낭비하거나 재할당/복사하는 경우가 많기 때문입니다 .이는 1번과도 연계되서 종종 이슈가 됩니다.
우선 std set의 성능은 hash_set에 비해서 이론적으로는 Ln vs 1 정도로 떨어지지만 경험적으로는 그정도 차이가 나지는 않습니다.
따라서, 기본적으로 set을 정확히 어떻게 사용했는지를 알지 못하면 답변을 드리기 어려운 면이 있습니다. :-)