C ++ 프로그램에서 scanf ()를 사용하는 것이 cin을 사용하는 것보다 빠릅니다?


126

이것이 사실인지는 모르겠지만 사이트를 제공하는 문제 중 하나에 대한 FAQ를 읽고있을 때주의를 환기시키는 무언가를 발견했습니다.

입력 / 출력 방법을 확인하십시오. C ++에서는 cin과 cout을 사용하는 것이 너무 느립니다. 이것을 사용하면 적절한 양의 입력 또는 출력으로 문제를 해결할 수 없다는 것을 보장합니다. 대신 printf 및 scanf를 사용하십시오.

누군가 이것을 명확히 할 수 있습니까? 실제로 C ++ 프로그램에서 scanf () 를 사용하는 것이 cin >> something을 사용하는 것보다 빠릅 니까? 그렇다면 C ++ 프로그램에서 사용하는 것이 좋은 방법입니까? 나는 C ++을 배우고 있지만 C 특정이라고 생각했습니다.


14
내 생각 엔 나쁜 프로그래머는 성능 저하로 표준 라이브러리를 비난합니다. 항상 유머러스 한 "GCC에서 버그를 발견 한 것 같아요"라는 외침처럼.
John Kugelman

11
@eclipse : 제가 대회를 위해 작업 한 ACM 문제에는 상당한 양의 입력 / 출력이 있으며 프로그램은 60 초 이내에 문제를 해결해야합니다. 여기에서 실제 문제가됩니다.
mpen 2009-06-25

19
--- 즉, 추가 성능 향상을 위해 scanf ()에 의존해야하는 경우 문제가 잘못된 방식으로 진행됩니다. :)
mpen

4
관찰처럼-나는 그것을 가지고 놀았고 두 번째 문제 (PRIME1)에서-동일한 알고리즘을 사용하여 한 번은 cin / cout을 사용하고 한 번은 scanf / printf를 사용하고 첫 번째 버전은 두 번째 버전보다 빠릅니다 (그러나 통계적으로 관련이 없을 정도로 가깝습니다.) 이는 입출력 집약적이라는 문제점 중 하나이며 입출력 방식은 통계적 차이가 전혀 없었다.
Eclipse

4
@Eclipse-두 가지 방법 테스트에 대한 정보에 감사드립니다. 그래도 난 슬프다 - 나는 CIN 비난과 cout을하려고했으나 지금은 내 알고리즘 : 짜증 것을 알고있다
zeroDivisible

답변:


209

다음은 간단한 경우에 대한 간단한 테스트입니다. 표준 입력에서 숫자 목록을 읽고 모든 숫자를 XOR하는 프로그램입니다.

iostream 버전 :

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

scanf 버전 :

#include <stdio.h>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (1 == scanf("%d", &x))
    parity ^= x;
  printf("%d\n", parity);

  return 0;
}

결과

세 번째 프로그램을 사용하여 33,280,276 개의 난수가 포함 된 텍스트 파일을 생성했습니다. 실행 시간은 다음과 같습니다.

iostream version:  24.3 seconds
scanf version:      6.4 seconds

컴파일러의 최적화 설정을 변경해도 결과가 전혀 바뀌지 않는 것 같습니다.

따라서 실제로 속도 차이가 있습니다.


편집 : 사용자 clyfish 는 속도 차이가 주로 CI / O 기능과 동기화를 유지하는 iostream I / O 기능으로 인해 발생 한다고 지적 합니다. 다음을 호출하여이 기능을 끌 수 있습니다 std::ios::sync_with_stdio(false);.

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  std::ios::sync_with_stdio(false);

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

새로운 결과 :

iostream version:                       21.9 seconds
scanf version:                           6.8 seconds
iostream with sync_with_stdio(false):    5.5 seconds

C ++ iostream이 승리합니다! 이 내부 동기화 / 플러싱은 일반적으로 iostream i / o를 느리게하는 것으로 밝혀졌습니다. stdio와 iostream을 믹싱하지 않는 경우 끄면 iostream이 가장 빠릅니다.

코드 : https://gist.github.com/3845568


6
'endl'을 사용하면 실행 속도가 느려질 수 있다고 생각합니다.
크리슈나 모한

2
std :: endl의 사용은 루프에 없습니다.
nibot

동기화를 켜거나 끄더라도 차이가 없습니다. libc ++를 비난하십시오. libstdc ++ 만 향상
iBug

<cstdio>와 <stdio.h> 사이에 어떤 차이가 있다고 생각하십니까 ??
Chandrahas Aroori

iostream한 번의 scanf호출 에서 둘 이상의 정수를 구문 분석하면 손실됩니다 .
Maxim Egorushkin

68

http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma

cin/의 성능은 cout기본 C 라이브러리와 동기화 상태를 유지해야하기 때문에 느려질 수 있습니다. 이것은 C IO와 C ++ IO가 모두 사용되는 경우 필수적입니다.

그러나 C ++ IO 만 사용하려는 경우 IO 작업 전에 아래 줄을 사용하면됩니다.

std::ios::sync_with_stdio(false);

이에 대한 자세한 정보는 해당 libstdc ++ 문서를 참조하십시오 .


그리고 정말 cstdio 거의 빨리 iostream을 (표준 : IOS :: sync_with_stdio (false)를) 그냥 위의 라인을 확인
gabrielhidasy

또한 cin.tie (static_cast <ostream *> (0)); 성능 향상을 위해
Mohamed El-Nakib 2014-02-05

42

아마도 scanf는 스트림을 사용하는 것보다 다소 빠릅니다. 스트림은 많은 형식 안전성을 제공하고 런타임에 형식 문자열을 구문 분석 할 필요가 없지만 일반적으로 과도한 메모리 할당이 필요하지 않다는 장점이 있습니다 (이는 컴파일러 및 런타임에 따라 다름). 즉, 성능이 유일한 최종 목표이고 중요한 경로에 있지 않는 한 더 안전한 (더 느린) 방법을 선호해야합니다.

허브 셔터 "에 의해 여기에 기록 된 아주 맛있는 기사가 매너 농장의 문자열 포매터 상기와 같은 문자열 포맷터의 성능 세부의 많은으로 간다" sscanflexical_cast어떤 종류의 일들을하고 있었다 그들이 느리게 또는 빠르게 실행. 이것은 아마도 C 스타일 IO와 C ++ 스타일 사이의 성능에 영향을 미치는 것들과 유사합니다. 포맷터와의 주요 차이점은 유형 안전성과 메모리 할당 수입니다.


19

저는 UVa Online의 문제를 해결하기 위해 저녁을 보냈습니다 (매우 흥미로운 문제인 Factovisors, 확인해보세요).

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=35&page=show_problem&problem=1080

제출물에 TLE (시간 제한 초과)가 발생했습니다. 이러한 문제 해결 온라인 심사 사이트에서는 솔루션을 평가하는 데 사용되는 수천 개의 테스트 사례를 처리하는 데 약 2-3 초의 시간 제한이 있습니다. 이와 같은 계산 집약적 인 문제의 경우 매 마이크로 초가 중요합니다.

제안 된 알고리즘을 사용하고 있었지만 (사이트에 대한 토론 포럼에서 읽음) 여전히 TLE를 얻었습니다.

"cin >> n >> m"을 "scanf ("% d % d ", & n, & m)"로 변경하고 몇 개의 작은 "couts"를 "printfs"로 변경하고 TLE가 "Accepted"로 변경되었습니다!

예, 특히 시간 제한이 짧은 경우 큰 차이를 만들 수 있습니다.


동의하다. 동일은 UVA 온라인 판사 문제에 나에게 일어난 육군 대화 상대의 uva.onlinejudge.org/...
모하메드 엘 - Nakib을

6

성능과 문자열 형식 모두에 관심이 있다면 Matthew Wilson의 FastFormat 라이브러리를 살펴보십시오 .

편집-해당 라이브러리의 accu 출판물에 대한 링크 : http://accu.org/index.php/journals/1539


완전히 동의합니다. 그러나 FastFormat은 출력 전용이라는 것을 알고 있어야합니다. 입력 / 읽기 기능이 없습니다. (아직, 어쨌든)
dcw

불행히도 그 링크는 죽은 것 같습니다. 다음은 뒤로 기계 사본입니다 : web.archive.org/web/20081222164527/http://fastformat.org
nibot

2

FILE *을 C ++ streambuf로 구현하고 fprintf를 런타임 형식 파서로 구현하는 stdio 구현 ( libio )이 있습니다. IOstreams는 런타임 형식 구문 분석이 필요하지 않으며 모두 컴파일 타임에 수행됩니다. 따라서 백엔드가 공유되면 iostreams가 런타임에 더 빠르다고 기대하는 것이 합리적입니다.


나는 그렇게 생각하지 않는다. 나는 GNU의 libc가 순수한 C와 어셈블리라고 생각합니다.
Chris Lutz

2

예 iostream은 cstdio보다 느립니다.
예, C ++로 개발하는 경우 cstdio를 사용하지 않아야합니다.
하지만 포맷, 타입 안전성, blah, blah, blah ...에 신경 쓰지 않는다면 scanf보다 I / O를 얻는 더 빠른 방법이 있습니다.

예를 들어 이것은 STDIN에서 숫자를 가져 오는 사용자 지정 루틴입니다.

inline int get_number()
{
    int c;        
    int n = 0;

    while ((c = getchar_unlocked()) >= '0' && c <= '9')
    {
        // n = 10 * n + (c - '0');
        n = (n << 3) + ( n << 1 ) + c - '0';
    }
    return n;
}

1
getchar_unlocked ()는 GCC하지 비주얼 스튜디오에 대한 표준이 아닌, 그리고 볼 수 있습니다
모하메드 엘 - Nakib

2

명령문 cincout일반적인 사용에서보다 느린 것 같습니다 scanfprintfC ++에서하지만, 실제로는 더 빠르다!

문제는 다음과 같습니다. C ++에서는 cinand 를 사용할 때마다 cout기본적으로 동기화 프로세스가 발생하여 scanfcin프로그램에서 둘 다 사용하면 둘 다 서로 동기화되어 작동 하는지 확인합니다 . 이 동기화 프로세스에는 시간이 걸립니다. 따라서 cincout느린 것으로 나타났습니다.

그러나 동기화 프로세스가 발생하지 않도록 설정된 경우 cin는보다 빠릅니다 scanf.

동기화 프로세스를 건너 뛰려면 프로그램의 시작 부분에 다음 코드 스 니펫을 포함하세요 main().

std::ios::sync_with_stdio(false);

자세한 내용은 이 사이트 를 방문 하십시오.


동기화에 대한 설명을 +1하십시오. 방금 동기화를 끄고 일부 코드에서 scanf와 cin을 모두 사용했습니다 . 이제 나는 그것에 무엇이 잘못되었는지 압니다. 감사합니다!
Dariush

1

문제는 호출 cin위에 추상화 계층을 제공하기 때문에 많은 오버 헤드가 수반된다는 것 scanf()입니다. C ++ 소프트웨어를 작성 scanf()하는 cin경우 over 사용해서는 안됩니다 cin. 성능을 원한다면 어쨌든 C ++로 I / O를 작성하지 않을 것입니다.


2
인가 cin보다 (런타임) 정말 더 "추상적" scanf? 나는 그렇게 생각하지 않는다 ... scanf런타임에 형식 문자열을 해석해야하지만 iostream컴파일 타임에는 형식을 알고 있습니다.
nibot 2014

1
@nibot 다음 유형은 컴파일 타임에 알려진 것이 아니라 형식을 . 예를 들어 입력이 16 진수로 예상되는지 여부는 런타임에std::istream 구성되는 방법 (I / O 조작기를 통해 또는 객체 자체 에 플래그를 설정 하여 ) 에 따라 전적으로 달라집니다 . 반면에 객체에는 그러한 상태가 없으므로 이와 관련하여 호출 이 훨씬 더 안정적입니다. istreamFILE*scanf
dreamlax

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

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

static int scanuint(unsigned int* x)
{
  char c;
  *x = 0;

  do
  {
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while(c<'0' || c>'9');

  do
  {
      //*x = (*x<<3)+(*x<<1) + c - '0';
      *x = 10 * (*x) + c - '0';
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while ((c>='0' && c<='9'));

  return 0;
}

int main(int argc, char **argv) {

  int parity = 0;
  unsigned int x;

  while (1 != (scanuint(&x))) {
    parity ^= x;
  }
  parity ^=x;
  printf("%d\n", parity);

  return 0;
}

파일 끝에 버그가 있지만이 C 코드는 더 빠른 C ++ 버전보다 훨씬 빠릅니다.

paradox@scorpion 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6  make test        
time ./xor-c < rand.txt
360589110

real    0m11,336s
user    0m11,157s
sys 0m0,179s
time ./xor2-c < rand.txt
360589110

real    0m2,104s
user    0m1,959s
sys 0m0,144s
time ./xor-cpp < rand.txt
360589110

real    0m29,948s
user    0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < rand.txt
360589110

real    0m7,604s
user    0m7,480s
sys 0m0,123s

원래 C ++는 30 초, C 코드는 2 초가 걸렸습니다.


-1

물론 iostream보다 cstdio를 사용하는 것은 우스꽝 스럽습니다. 적어도 소프트웨어를 개발할 때 (이미 c를 통해 C ++를 사용하고 있다면 단점으로 만 고통받는 대신 계속해서 장점을 사용하십시오).

그러나 온라인 심사에서는 소프트웨어를 개발하는 것이 아니라 Microsoft 소프트웨어가 3 초 만에 달성하는 데 60 초가 걸리는 작업을 수행 할 수있는 프로그램을 만들고 있습니다 !!!

따라서이 경우 황금률은 다음과 같습니다 (물론 Java를 사용하여 더 많은 문제를 겪지 않으면)

  • C ++를 사용하고 모든 힘 (무거움 / 느림)을 사용하여 문제를 해결합니다.
  • 시간이 제한되면 printfs 및 scanfs에 대한 cins 및 couts를 변경하십시오 (클래스 문자열을 사용하여 망가진 경우 다음과 같이 인쇄하십시오. printf (% s, mystr.c_str ());
  • 여전히 시간이 제한되어 있다면 몇 가지 명백한 최적화를 시도하십시오 (예 : 너무 많은 내장 된 for / while / dowhiles 또는 재귀 함수를 피하십시오). 또한 너무 큰 참조 객체를 통과해야합니다.
  • 여전히 시간이 제한되어 있다면 std :: vector 및 c-array 세트를 변경해보십시오.
  • 여전히 시간이 제한되어 있다면 다음 문제로 넘어가십시오 ...

-2

scanf보다 빠르 더라도 cin중요하지 않습니다. 대부분의 경우 하드 드라이브 또는 키보드에서 읽습니다. 걸리는 것보다 응용 프로그램에 원시 데이터를 얻는 것은 더 많은 시간을 크기 순서를 취 scanf하거나 cin이를 처리 할 수 있습니다.


파이프를 통한 IPC는 어떻습니까? 눈에 띄는 성능 저하가있을 수 있다고 생각하십니까?
dreamlax 2009-06-25

파이프를 통한 IPC를 사용하더라도 scanf / cin으로 커널을 구문 분석하는 것보다 커널에 들어오고 나가는 데 훨씬 더 많은 시간이 소요됩니다.
Jay Conrod

8
나는이 영역에서 테스트를했고 확실히 성능을 빤다. 사용자 입력의 경우 무시해도되지만 성능이 중요한 경우에는 그렇지 않습니다. 하지만 더 빠른 다른 C ++ 프레임 워크가 있습니다.
Johannes Schaub-litb 2009-06-25

문제는 즉 iostream 이다 하드 디스크보다 느립니다. 예, 그렇게 많이 짜증납니다.
polkovnikov.ph
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.