stringstream :: 2007/12/04 15:12
|
|
그래서 이번에는 문자열을 숫자로 바꾸는 작업을 stringstream을 써서 해보기로 했습니다. 거의 비슷하게 하면 될것 같더군요.
#include <iostream>
#include <sstream>
using namespace std;
long long stoll(const string& v) {
stringstream ss;
ss << v;
long long ret;
ss >> ret;
return ret;
}
int main() {
long long r;
for ( int i = 0; i < 10000000; ++i ) {
r = stoll("48623948209834029");
}
}
네. 이렇게 구현한 코드는 아주 그럴싸하게 잘 돌아갑니다. 나름대로 'C++'적이기도 하지요. 하지만 여러 가지 이유로 저는 위의 코드를 사용하기를 포기했습니다. 왜 그랬냐고요? 위의 코드를 Unix time 명령과 함께 실행해보면, 다음과 같은 결과를 얻습니다.
[bjlee@bjlee-xnote test]$ time ./a.out
real 0m24.972s
user 0m23.349s
sys 0m0.060s
[bjlee@bjlee-xnote test]$
하지만 같은 프로그램을 stdlib.h에 선언되어 있는 atoll을 써서 다음과 같이 하면 어떨까요?
int main() {
long long r;
for ( int i = 0; i < 10000000; ++i ) {
r = atoll("48623948209834029");
}
}
그랬을 경우에는 다음과 같은 결과를 얻습니다.
[bjlee@bjlee-xnote test]$ time ./a.out
real 0m2.306s
user 0m2.276s
sys 0m0.008s
[bjlee@bjlee-xnote test]$
간단한 실험으로도 성능 차이가 꽤 크다는 것을 확인할 수 있습니다. 객체 생성과 같은 기타등등의 오버헤드가 끼어들기 때문에 stringstream쪽의 퍼포먼스가 더 안좋게 나온 것이겠죠. 그럼 Bjarne 아저씨는 int를 string쪽으로 변환하는 가장 간단한 방법으로 왜 stringstream을 추천한 것일까요? 그게 궁금해서 반대쪽도 실험을 해봤습니다.
#include <iostream>
#include <sstream>
using namespace std;
string itos(const long long& v) {
stringstream ss;
ss << v;
string ret;
ss >> ret;
return ret;
}
int main() {
string p;
for ( int i = 0; i < 10000000; ++i ) {
p = itos(324879829);
}
}
위의 코드의 경우 time으로 시간을 재 보면 결과는 다음과 같습니다.
[bjlee@bjlee-xnote test]$ time ./a.out
real 0m19.790s
user 0m19.677s
sys 0m0.012s
[bjlee@bjlee-xnote test]$
그럼 다음과 같은 코드를 사용해 보면 어떨까요?
#include <iostream>
#include <sstream>
using namespace std;
string itos(const long long& v) {
char buffer[256];
sprintf(buffer, "%lld", v);
return string( buffer );
}
int main() {
string p;
for ( int i = 0; i < 10000000; ++i ) {
p = itos(324879829);
}
}
이 경우의 성능을 재 보면 다음과 같습니다.
[bjlee@bjlee-xnote test]$ time ./a.out
real 0m7.683s
user 0m7.104s
sys 0m0.032s
[bjlee@bjlee-xnote test]$
어쨌던 stringstream을 쓰게 되면 성능은 배 이상 떨어지는군요.
그렇다면 Bjarne 아저씨가 int를 string으로 변환할 때 stringstream을 쓰는게 가장 간단하다고 추천한 이유는 무엇일까요? 이유도 간단합니다. string을 int로 변환하는 쪽은 그럴듯한 솔루션이 이미 많이 있습니다. (atoll이나 atoi와 같은) 거기다 이런 함수들은 '더 이상 간단해질 수 없을 정도로' 단순한 함수들이어서, 그 함수가 '그다지 우아해보이지 않는다는 이유로' C++적인 해결책을 고안해 낸다는 것이 말이 되질 않아요. 방금 위에서 실험한 결과로도 증명되는 이야기입니다만, 성능도 더 형편없어질 가능성이 높죠.
반면 int를 string으로 변환하는 방법을 찾아보면, 그다지 간단한 것이 없습니다. 제가 선호하는 방법은 (위의 예제 코드에서도 써먹었습니다만) sprintf를 쓰는 것인데, 그것도 버퍼를 잡는 등의 사전 작업이 필요하니까 그렇게 편하지는 않아요. 그러니 그런 경우에는 'stringstream'을 쓰는게 나을 수도 있겠죠. Bjarne의 말처럼, '간단함'이라는 견지에서 보면 말이에요. 거기다 stringstream 클래스 안에는 오만가지 데이터타입을 다 처리할 수 있도록 오버로딩된 연산자들이 잔뜩 구현되어 있습니다. 성능은 안좋을지 몰라도, "%lld같은 옵션을 전부 기억하고 있지 않아도 구현을 할 수 있으니까, 편하다는 것이죠. (거기다 template을 섞어쓰면 아마 더 편해질겁니다 ㅋㅋ)
그렇다면 오늘의 결론은...
- int -> string 변환이 필요한 경우에는 stringstream을 쓰는 것이 '간단하'다.
- 반대의 변환이 필요한 경우에는 stringstream을 써 봐야 코드가 간단해 지지 않는다
- 두 가지 경우 모두, C API를 쓰는 것에 비해서는 성능이 굉장히 떨어진다
성능이 걱정되신다면 뭐니뭐니 해도 C API쪽이 낫겠어요.