코딩 테스트를 풀다보면 시간 초과가 나는 경우가 많습니다.

그 이유는 다양하겠지만, 시간을 줄이는 첫번째 단계는 입력과 출력 시간을 줄여주는 것이라 생각합니다.

 

저는 자바로 코딩 테스트 할 때, 단순한 입출력이 아닌 버퍼를 사용해 입력과 출력을 받아줬습니다.

C#을 공부하면서 C#에서 빠른 입출력을 원할 때 어떤 방법을 써야하는지 공부한 내용을 정리하겠습니다.


백준 2741번으로 테스트를 해봤습니다.

 

https://www.acmicpc.net/problem/2741

 

2741번: N 찍기

자연수 N이 주어졌을 때, 1부터 N까지 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

www.acmicpc.net

 

문제는 간단히 숫자 N을 입력받으면, 1부터 N까지 출력하는 문제입니다.

이 문제에서 N의 범위가 최대 100,000이고, 시간 제한은 1초입니다.

 

먼저 가장 기본적으로 입출력을 해보겠습니다.

 

[사진 1] 기본

가장 기본적인 입출력 방식으로 문제를 풀어봤습니다.

 

[사진 2] 기본 결과

시간 초과가 났습니다.

사실 굳이 백준에서 검사하지 않고,

그냥 실행해서 100000을 입력해보면 딱 봐도 1초가 넘는 것을 볼 수 있습니다.

 

다음 방법은 StringBuilder를 사용하는 방식입니다.

 

[사진 3] StringBuilder

값을 StringBuilder에 저장한 후, 한번에 StringBuilder를 출력하는 방식입니다.

 

[사진 4] StringBuilder 결과

1초 안에 성공적으로 실행하였습니다.

하나씩 출력하지 않고 StringBuilder로 한번에 출력하는 것이 더 빠른 것을 확인할 수 있습니다.

문자열 양이 많아질수록 String보다 StringBuilder가 성능이 좋습니다.

물론, 각각 장단점이 있기 때문에 적절한 상황에 사용하는 것이 좋습니다.

 

입력도 빠르게 받을 수 있으면 실행 시간이 더 단축되지 않을까요?

여기서 Stream을 사용해서 입출력을 더 빠르게 받을 수 있습니다.

 

[사진 5] Stream

StreamReader와 StreaemWriter를 사용해서 입출력을 해봤습니다.

두 클래스를 사용하기 위해선 using System.IO;를 해줘야 합니다.

 

[사진 6] Stream 결과

StringBuilder보다 더 빠른 결과를 보입니다.

이 문제는 입력을 한번 밖에 받지 않기 때문에 큰 차이는 없지만,

입력을 여러번 받는다면 이 차이는 더 커질 것 입니다.

 

그럼 여기서 Stream은 무엇이고, 그 안에 사용된 다른 코드들은 무엇인지 알아보겠습니다.


Stream

스트림을 간단히 말하면,

데이터를 읽고 쓸 때, 데이터가 이동하는 경로입니다.

바이트 단위로 읽고 씁니다.

 

실생활로 예를 들어보면,

수도꼭지에 고무관을 끼워봅니다.

그러면 물은 수도꼭지에서 시작돼서 고무관으로 흐른 후, 밖으로 나옵니다.

여기서 물이 데이터, 고무관이 스트림으로 생각하면 될 것 같습니다.

 

Stream 클래스는 모든 스트림의 추상 기본 클래스입니다.

Stream 클래스는 FileStream, NetworkStream, MemoryStream, BufferedStream이 있습니다.

저희는 BufferedStream에 대해 알아보겠습니다.

 

BufferedStream

BufferedStream은 자바의 BufferedReader/BufferedWriter와 비슷합니다.

버퍼 크기만큼 데이터를 저장했다가 한번에 읽고 씁니다.
기본 버퍼 사이즈는 (1024 * 4 = 4096)바이트입니다.

 

실생활로 예를 들면,

물건을 하나씩 옮기는 것보다,

수레에 담을 수 있는 양만큼 실은 후 한번에 옮기는 것이 더 빠릅니다.

물건이 데이터, 수레가 버퍼라 생각하면 됩니다.

 

그래서 버퍼링 되지 않은 작업보다 버퍼링 된 작업이 더 빠릅니다.

 

OpenStandardInput()은 표준 입력 스트림을 가져오고,
OpenStandardOutput()은 표준 출력 스트림을 가져오는 메소드입니다.

StreamReader/StreamWriter는 데이터를 스트림에 읽고 쓰는데 사용됩니다.

 

그래서,

StreamWriter sw = new StreamWriter(new BufferedStream(Console.OpenStandardOutput()));
StreamReader sr = new StreamReader(new BufferedStream(Console.OpenStandardInput()));

를 해석하면,

"콘솔의 표준 입출력을 버퍼를 이용해 스트림에 데이터를 읽거나 쓴다."가 저의 해석입니다.

 

Stream을 사용할 때 주의할 점이 있습니다.

Stream 사용이 모두 끝났으면 Close()로 메모리를 반환해야 합니다.

Close()는 메모리를 반환하기 전에 자동으로 Flush()를 호출합니다.

Close()를 호출해주지 않으면 원하는 출력이 되지 않을 수 있습니다.

여기서 Flush()는 데이터를 모두 내보내는 메소드입니다.

 

사용방법 입니다.

 

sr.Read(); //한 문자씩 읽기
sr.ReadLine(); //한 줄씩 읽기
sr.ReadToEnd(); //전체를 한 번에 읽기

sw.Write(str); //str을 스트림에 쓴다. 
sw.WriteLine(str); //str을 스트림에 쓴다. 뒤에 개행 문자가 추가된다. 매개변수가 없으면 줄바꿈만 수행한다.


제가 공부한 부분을 정리한 내용이기 때문에 틀린 부분 있을 수 있습니다!!

혹시 틀린 부분이 있으면 알려주세요!!

'C#' 카테고리의 다른 글

[C#] 다형성, virtual과 override  (0) 2022.09.06
[C#] 상속을 해보자  (0) 2022.08.27
[C#] 암묵적 구현과 명시적 구현  (0) 2022.08.23
[C#] 추상 클래스와 인터페이스  (0) 2022.08.19
[C#] 객체지향 언어에 대하여(OOP)  (0) 2022.08.10

+ Recent posts