1. Buffer(버퍼)를 사용하는 이유

더보기
  • 버퍼는 여러 입출력 장치간에 데이터를 읽고 쓰는 동안 발생하는 속도 차이를 해결한다.
  • 1,000 바이트를 읽는다고 가정할 때, 1바이트를 여러 번 읽을 경우 CPU 사용횟수와 메모리 접근시 평균 탐색시간이 누적되어 한 번에 1000 바이트를 읽는 것 보다 시간이 오래 걸린다.
    • 1바이트씩 1,000번 읽기
    • 1,000바이트씩 1번 읽기
  • 보통 8KB(8192Byte) 정도의 버퍼 크기를 유지한다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE 1000000  // 100만 바이트 (1MB)

void read_byte_by_byte(const char* filename) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        perror("파일 열기 실패");
        return;
    }

    int ch;
    size_t count = 0;

    clock_t start = clock();

    while ((ch = fgetc(fp)) != EOF && count < SIZE) {
        count++;
    }

    clock_t end = clock();
    fclose(fp);

    printf("1바이트씩 %zu번 읽기: %.4f초\n", count, (double)(end - start) / CLOCKS_PER_SEC);
}

void read_all_at_once(const char* filename) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        perror("파일 열기 실패");
        return;
    }

    char buffer[SIZE];

    clock_t start = clock();

    size_t bytes_read = fread(buffer, 1, SIZE, fp);

    clock_t end = clock();
    fclose(fp);

    printf("%zu바이트를 한 번에 읽기: %.4f초\n", bytes_read, (double)(end - start) / CLOCKS_PER_SEC);
}

int main() {
    const char *test_file = "testfile.bin";

    // 테스트용 1MB 더미 파일 생성
    FILE *fp = fopen(test_file, "wb");
    for (int i = 0; i < SIZE; i++) {
        fputc('A', fp);  // A 1바이트씩 쓰기
    }
    fclose(fp);

    printf("=== 버퍼 사용에 따른 읽기 속도 비교 ===\n\n");

    read_byte_by_byte(test_file);
    read_all_at_once(test_file);

    return 0;
}

// === 버퍼 사용에 따른 읽기 속도 비교 ===

// 1바이트씩 1000000번 읽기: 0.0021초
// 1000000바이트를 한 번에 읽기: 0.0001초

 

2. Buffer(버퍼)란?

더보기
  • 영어로 '완충제' 이다.
  • Buffer(버퍼)는 스트림과 같은 데이터의 가상 연결 통로에서
    데이터를 읽고 쓰는 동안 발생하는 속도 차이를 해결하기 위해
    데이터가 임시 저장되는 메모리의 물리적 공간을 의미하는 용어다.

 

3. Buffer(버퍼)를 사용한 화면 출력 예시

더보기

 

입력 버퍼: 사용자와 저장장치의 입력은 프로그램의 연산 속도보다 훨씬 느리다.

프로그램이 이러한 입력의 완료를 대기하는 것은 낭비가 심하다.

 

출력 버퍼: 모니터의 출력은 프로그램의 연산 속도보다 매우 느리다.

프로그램이 모니터 출력 완료를 대기하는 것 또한 엄청난 낭비다.

*예) 컴퓨터 게임에서 화면 주사율 딜레이 및 수직 동기화 백버퍼, 프론트 버퍼 이슈가 발생하는 경우

 

: 프로그램의 연산 속도는 입력과 출력 시간에 비해 빠르다.

그래서 입력 버퍼와 출력 버퍼를 사용해 딜레이 문제를 보완한다.

 

4. Buffer 적용 예시

더보기

개발자가 소스코드 레벨에서 버퍼를 직접 구현하는 것도 한가지 방법이지만, 프로그래밍 언어가 제공하는 '표준 입출력 라이브러리'를 사용하면,  내부에 버퍼링 구현체를 손쉽게 사용 할 수 있다.

 

 

C언어, 표준 입출력 stream

  • C언어, <stdio.h> 라이브러리를 사용해 표준 입출력을 사용한다.
    printf( ) - 스트림과 연결된 버퍼 구현체 - stdout stream  
    scanf( ) - 스트림과 연결된 버퍼 구현체 - stdin stream 

  • printf("Hello") 수행 시
    • stdout 스트림에 "Hello" 전달
    • 버퍼에 저장됨 (즉시 출력되지 않음)
    • 개행(\n), fflush(), 버퍼가 가득 찼을 때 → 화면으로 출력

 

C++, 표준 입출력 stream

  • C++, <iostream> 라이브러리를 사용해표준 입출력을 사용한다.
    cin 객체
    cout 객체

 

C/C++ 언어의 표준 입출력 라이브러기 기반  함수들은 내부적으로 입출력 버퍼를 사용하여 데이터를 처리한다. 

표준 스트림을 사용하는 입출력 버퍼 구현체는 겉으로 드러나지 않는다. 

다음과 같은 buffer 구현체는 표준 입출력 라이브러리가 사용하는 stdin, stdout, stderr 스트림의 버퍼가 아니다.

 

char buffer[100];
gets_s(buffer, sizeof(buffer));

 

5. "buffer 를 비운다"는 의미

더보기

입력 버퍼 사용 관점에서,

사용자가 데이터를 입력하면, 프로그램으로 바로 전달되는 것이 아니라 우선 입력버퍼에 데이터가 저장된다.

그리고 엔터 키를 치거나, 개행문자(\n)가 입력버퍼로 들어오거나, 입력버퍼가 가득차면,

버퍼를 비우면서 데이터를 프로그램으로 전달한다. ​

 

출력 버퍼 관점에서

프로그램에서 출력장치(모니터 등)로 데이터가 바로 전달되지 않고 우선 출력버퍼에 데이터가 저장된다.

그리고 개행문자(\n)가 출력버퍼로 들어오거나, 출력버퍼가 가득차면 버퍼를 비우면서 데이터를 출력장치로 전달한다. ​ ​

 

"buffer 를 비운다" 는 의미는, "버퍼에 저장된 데이터가 버퍼를 떠나 목적지로 전송된다"는 의미에 가깝다.

 

6. 버퍼 사이즈 조절 예시

더보기
setvbuf(파일포인터, 사용자지정입출력버퍼, 모드, 크기);
int setvbuf(FILE *_Stream, char *_Buffer, int _Mode, size_t _Size);
// 설정 변경에 성공하면 0을 반환, 실패하면 0이 아닌 값을 반환

 

예) 문장이 한번에 출력된다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("How");
    sleep(1);
    printf(" are");
    sleep(1);
    printf(" you?");
    sleep(1);
    printf("\n");  // 문장이 쌓인 후 한번에 출력된다.
    exit(0);
}

 

예2) 문장이 버퍼에 쌓이는 경우 비교

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 1024

int main()
{
    // 1. 버퍼 없음 (즉시 출력)
    setbuf(stdout, NULL);
    printf("[무버퍼링] ");
    printf("버퍼에"); sleep(1);
    printf(" 쌓이지"); sleep(1);
    printf(" 않는다."); sleep(1);
    printf("\n");

    // 2. 버퍼 있음 (출력 지연됨)
    char buf[BUFFER_SIZE];
    setbuf(stdout, buf); // 사용자 버퍼 설정

    printf("\n[버퍼링] ");
    printf("문장이!"); sleep(1);
    printf(" 버퍼에"); sleep(1);
    printf(" 쌓인다.!!\n"); sleep(1);

    // 버퍼에 출력이 쌓여 있었기 때문에 아직 아무것도 화면에 보이지 않음
    // 이제 강제로 출력
    fflush(stdout); // 출력 강제

    // 3. 다시 무버퍼링
    setbuf(stdout, NULL);
    printf("\n[다시 무버퍼링] ");
    printf("How"); sleep(1);
    printf(" are"); sleep(1);
    printf(" you?"); sleep(1);
    printf("\n");

    return 0;
}