일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- UnrealEngine5
- GeeksForGeeks
- 2294
- const
- UnrealEngine4
- Programmers
- 프로그래머스
- baekjoon
- 티스토리챌린지
- C++
- algorithm
- RVO
- 줄 세우기
- 백준
- UE5
- RootMotion
- IFileDialog
- winapi
- C
- directx
- 언리얼엔진5
- 1563
- Frustum
- DirectX11
- 팰린드롬 만들기
- NRVO
- 오블완
- Unreal Engine5
- softeer
- DeferredRendering
- Today
- Total
Game Develop
[C++] String 성능에 관하여 본문
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
|
string addString(string s)
{
string result;
for (int i = 0; i < s.size(); i++)
{
if (s[i] >= 0x20) // 출력가능한 문자열이면
{
result = result + s[i]; // 1번
//result += s[i]; // 2번
}
}
return result;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string input;
string h = "1234567812324232414142421412421521512232414142421412421521512232414142421412421521512232414142421412421521512141424214124215215124123125215214";
long time = 0;
for (int i = 0; i < 100; ++i)
{
auto start = chrono::high_resolution_clock::now();
addString(h);
auto end = chrono::high_resolution_clock::now();
time += (end - start).count();
}
time = time / 100;
cout << "평균 : " << time << endl;
return 0;
}
|
cs |
위 코드는 addString함수의 성능을 테스트하는 코드이다.
addString 함수를 보면 출력가능한 문자들일 경우, 해당 코드를 실행하게된다.
0x20은 10진수로 32이고 아스키코드표를 보면 32부터 출력가능한 문자이다. (32는 Space, 공백이다)
위 코드를 수행하면 결과는 아래와 같다. 단위는 ns이다.
그러면 함수 내의 1번이라 표시한 코드를 주석처리하고, 2번을 주석처리를 해제하고 실행해보겠다. (result += s[i])
약 10배 차이난다. 물론 컴퓨터 하드웨어에 따라 차이는 좀 더 나거나 좁혀지긴 하겠지만, 어쨌든 크게 다르진 않을것이다.
위 캡처는 2만초반대긴한데 거의 1만후반대로 뜨는 것 같다.
result = result + s[i] ---> result += s[i]; // 10배의 성능향상.
무슨 차이가 있기에 이 간단한 코드수정으로 성능이 차이가 많이나는걸까?
먼저 result + s[i]라는 결과값을 임시로 담을 임시문자열객체를 만들기위해 메모리관리자를 호출하게 된다.
s의 사이즈가 100이라면 100번의 호출이 있게된다. 여기서 끝나는게 아니라, 해당 코드가 끝나면 임시문자열객체를 소멸시켜야하니 메모리관리자를 또 호출하게된다.
그러면 결과적으로 for문횟수 * 2번의 호출이 있게된다.
뭐든 메모리관리자를 호출하는 일은 없을수록 좋다고 배웠다. 프로그램 런타임시 new든 delete든 최대한 호출을 안해야한다. 그래서 오브젝트풀이라던가 하는 디자인패턴이 생기는것이고.
뿐만 아니라, result = result + s[i] 굵은 result에 원본크기만큼 복사를 해야되니, 결국 O(n^2)의 시간복잡도가 된다.(n은 문자열크기)
이런 과정들이 있다보니 성능이 10배가량 차이가 있다는 것.
성능개선
물론 2번을 사용한다고 하더라도 여전히 성능개선의 여지는 있다.
문자열은 내부적으로 고정된 버퍼를 가지고 있고 해당 버퍼의 크기를 벗어나면 새로운 버퍼를 할당하기위해 메모리관리자를 호출한다.
그렇기 때문에 처음에 크기를 알고 있다면 미리 할당해주는게 좋다. 불필요하게 메모리관리자를 호출 안해도 되기 때문이다.
그러면 addString함수의 변수 result의 크기를 매개변수 s의 크기로 미리 할당해주자.
확실히 이전보다 성능이 좀 더 향상했다. 추가적인 버퍼확장(버퍼재할당)을 위한 메모리관리자 호출이 없기 때문이다.
음.. 사실 이것외에도 이것저것 굉장히 많은데, 전부 다 위처럼 캡처하면서 하려니 배보다 배꼽이 커지는 것 같아서 그냥 텍스트로만 간단하게 쓰고 끝내야겠다.
1. 불필요한 매개변수 s의 복사를 방지하기 위해 매개변수를 const string& s로 선언.
2. 사실 결국 최고의 성능을 위해선 c스타일로 하는게 베스트... 그런거 아니며 위의 과정정도만 해도 괜찮지않을까...?
3. 함수의 성능속도를 체크하기엔 여러 변수가 있다. 위 코드들처럼 중간에 아무런 다른과정없이 해당함수만 호출할 경우 데이터가 캐시에 유지되기 때문에 아닌경우에 비해 더 빠르다.
이런 알찬내용을 제공해준 원본 링크. 한번은 꼭 읽어보길 추천.
https://junstar92.tistory.com/387
'C++ > C++' 카테고리의 다른 글
[C++] vector (0) | 2022.11.01 |
---|---|
[C++] map에서 원소 넣을때 insert도 활용하자. (0) | 2022.10.29 |
[C++] 포인터와 배열의 차이. (1) | 2022.10.08 |
[C++] Hash (0) | 2022.09.11 |
[C++] C++에서 배열초기화하기 (0) | 2022.09.08 |