Coding_practice/Know-How

[C++] C++ 코드 최적화 방법 정리

__Vivacé__ 2023. 3. 2. 00:33

문제 풀이 시, 입출력 속도를 향상시키기 위한 최적화 기법

 

키워드 정리

  • 스트림(Stream) : 컴퓨터 프로그래밍에서 데이터의 흐름을 나타내는 개념
    • 입력 스트림(Input Stream) : 데이터를 프로그램으로부터 읽어들이는 역할
    • 출력 스트림(Output Stream) : 프로그램에서 데이터를 출력하는 역할

 

  • 동기화(synchronization) : 여러 프로세스가 공유 자원을 사용할 때, 이 프로세스들이 서로 경쟁하지 않고 정상적으로 작동할 수 있도록 관리하는 것

 

  • 버퍼(buffer) : 컴퓨터의 메모리 영역에서 임시로 데이터를 저장하는 공간

 


문제 풀이 시, 입출력 속도를 향상시키기 위한 최적화 기법

#include <iostream>

using namespace std;

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cout << "test" << endl;
	cout << "test" << '\\n';
}

 

1. ios_base::sync_with_stdio(false) : 표준 I/O 스트림의 동기화 옵션을 비활성화

  • 표준 I/O 스트림 : 표준 I/O 라이브러리(stdio)와 표준 C++ I/O 라이브러리(iostream)을 통합하여 사용할 수 있는 스트림, 비활성화 시 두 스트림이 분리
  • 보통 알고리즘 문제를 풀 때, 대부분 single thread 환경이기 때문에 결과에 영향이 없고, C와 C++의 버퍼를 분리하기 때문에 속도가 빨라짐

 

2. cin.tie(NULL) & cout.tie(NULL) : 표준 입력 스트림 객체인 cin과 표준 출력 스트림 객체인 cout간의 연결을 끊음

  • 기본적으로 cin은 cout과 연결되어 있어, cin이 입력을 읽을 때 cout의 버퍼를 먼저 비워줌
  • cout이 출력한 데이터가 버퍼에 저장되지 않고, 바로 표준 출력 장치로 출력되어 속도 향상

 

3. ’\n’ vs endl

  • ’\n’ : 개행 문자 출력 (출력 버퍼를 비우지 않기에, 데이터가 바로 화면에 안 나타날 수 있음)
  • endl : 개행 문자 출력 + 출력 버퍼를 비움 → 딜레이가 발생

 


CPU register 활용하기

변수 선언 시, register 키워드를 사용하면, 변수가 RAM이 아닌 register에 할당된다.

 

*레지스터(register) : 프로세서 내부에 위치하는 작은 메모리 공간, 프로세서에서 빠르게 액세스할 수 있는 변수를 저장하기 위해 사용

#include <windows.h>
#include <stdio.h>

void main()
{
    int i;
    unsigned long long t = GetTickCount64();

    for (i = 0; i < 10000; i++) {
        printf("%d\\n", i);
    }

    t = GetTickCount64() - t;
    printf("%ld\\n", t);  // 4.547sec
}
// register 선언 방법
// register <자료형> <변수형>

#include <windows.h>
#include <stdio.h>

void main()
{
    register int i; // register에 변수를 request
    unsigned long long t = GetTickCount64();

    for(i=0; i<10000; i++){
        printf("%d\\n", i);
    }

    t = GetTickCount64() - t;
    printf("%ld\\n", t);  // 4.282sec
}

제약 사항

  1. 레지스터 크기가 작음(4~8 byte) : 크기가 작은 변수만 사용 가능
  2. 레지스터 변수는 주소를 갖지 않음 : 포인터 연산, 배열 인덱스 연산 불가
  3. 블록 범위에서만 사용 가능 : 전역 변수로 사용 불가
  4. 최신 컴파일러는 이 기능을 무시 : register 키워드는 요청일 뿐, 무조건 할당이 아님 / 잘못 쓰면 더 느려질 수도 있음
  5. for loop 등 반복 연산 횟수가 많은 변수에 사용할 때 추천