Sử dụng scanf () trong các chương trình C ++ có nhanh hơn sử dụng cin?


126

Tôi không biết điều này có đúng không, nhưng khi tôi đang đọc FAQ về một trong những vấn đề cung cấp trang web, tôi đã tìm thấy một cái gì đó, điều đó gây sự chú ý của tôi:

Kiểm tra phương thức nhập / xuất của bạn. Trong C ++, sử dụng cin và cout quá chậm. Sử dụng chúng, và bạn sẽ đảm bảo không thể giải quyết bất kỳ vấn đề nào với một lượng đầu vào hoặc đầu ra kha khá. Sử dụng printf và scanf thay thế.

Ai đó có thể vui lòng làm rõ điều này? Có thực sự sử dụng scanf () trong các chương trình C ++ nhanh hơn sử dụng cin >> gì không? Nếu có, đó có phải là một cách thực hành tốt để sử dụng nó trong các chương trình C ++ không? Tôi nghĩ rằng đó là C cụ thể, mặc dù tôi chỉ học C ++ ...


14
Tôi đoán: lập trình viên xấu đổ lỗi cho các thư viện tiêu chuẩn cho hiệu suất kém. Kiểu như luôn luôn hài hước "Tôi nghĩ rằng tôi đã tìm thấy một lỗi trong GCC".
John Kugelman

11
@eclipse: Các vấn đề ACM mà tôi đã làm cho các cuộc thi có số lượng đầu vào / đầu ra đáng kể và chương trình của bạn phải giải quyết các câu hỏi trong khoảng 60 giây ... nó trở thành một vấn đề thực sự ở đây.
mở

19
--- điều đó nói rằng, nếu bạn cần dựa vào scanf () để tăng hiệu suất bổ sung, bạn sẽ gặp vấn đề sai cách :)
mpen

4
Giống như một quan sát - tôi đã chơi xung quanh nó và về các vấn đề thứ 2 (PRIME1) - sử dụng cùng một thuật toán, cả hai lần, một lần sử dụng cin / cout và một lần với scanf / printf và phiên bản đầu tiên nhanh hơn lần thứ hai (nhưng đủ gần để thống kê không liên quan). Đây là một trong những vấn đề được đánh dấu là thâm dụng đầu vào / đầu ra và phương pháp đầu vào / đầu ra không có sự khác biệt thống kê nào.
Nhật thực

4
@Eclipse - cảm ơn thông tin về việc thử nghiệm cả hai phương pháp. Mặc dù tôi rất buồn - tôi đã cố đổ lỗi cho cin và cout, nhưng bây giờ tôi biết rằng thuật toán của mình thật tệ :)
zeroDivisible

Câu trả lời:


209

Đây là một bài kiểm tra nhanh về một trường hợp đơn giản: một chương trình đọc danh sách các số từ đầu vào tiêu chuẩn và XOR tất cả các số.

phiên bản 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;
}

phiên bản quét:

#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;
}

Các kết quả

Sử dụng chương trình thứ ba, tôi đã tạo một tệp văn bản chứa 33.280.276 số ngẫu nhiên. Thời gian thực hiện là:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

Thay đổi cài đặt tối ưu hóa của trình biên dịch dường như không thay đổi nhiều kết quả.

Như vậy: thực sự có sự khác biệt về tốc độ.


EDIT: Người dùng clyfish chỉ ra bên dưới rằng sự khác biệt về tốc độ phần lớn là do các chức năng I / O của iostream duy trì đồng bộ hóa với các chức năng CI / O. Chúng tôi có thể tắt điều này bằng một cuộc gọi đến 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;
}

Kết quả mới:

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

C ++ iostream thắng! Nó chỉ ra rằng đồng bộ hóa / tuôn ra nội bộ này là những gì thường làm chậm iostream i / o. Nếu chúng ta không trộn stdio và iostream, chúng ta có thể tắt nó đi, và sau đó iostream là nhanh nhất.

Mã: https://gist.github.com/3845568


6
Tôi nghĩ rằng việc sử dụng 'endl' có thể làm chậm quá trình thực thi.
Krishna Mohan

2
Việc sử dụng std :: endl không có trong vòng lặp.
nibot

Làm cho không có sự khác biệt với đồng bộ bật hoặc tắt. Đổ lỗi cho libc ++ vì điều đó. Nó chỉ tăng các libstdc ++
iBug

Bạn có nghĩ rằng sẽ có bất kỳ sự khác biệt nào giữa <cstdio> và <stdio.h> ??
Chandrahas Aroori

iostreammất khi bạn phân tích nhiều hơn một số nguyên trong một scanfcuộc gọi.
Maxim Egorushkin

68

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

Hiệu suất của cin/ coutcó thể chậm vì họ cần giữ đồng bộ với thư viện C bên dưới. Điều này rất cần thiết nếu cả C IO và C ++ IO sẽ được sử dụng.

Tuy nhiên, nếu bạn chỉ sử dụng C ++ IO, thì chỉ cần sử dụng dòng dưới đây trước bất kỳ hoạt động IO nào.

std::ios::sync_with_stdio(false);

Để biết thêm thông tin về điều này, hãy xem các tài liệu libstdc ++ tương ứng .


Chỉ cần kiểm tra dòng trên (std :: ios :: sync_with_stdio (false);) Và nó thực sự khiến iostream nhanh như cstdio
gabrielhidasy

cũng sử dụng cin.tie (static_cast <lasream *> (0)); để có hiệu suất tốt hơn
Mohamed El-Nakib

42

Có lẽ scanf có phần nhanh hơn so với sử dụng luồng. Mặc dù các luồng cung cấp rất nhiều loại an toàn và không phải phân tích các chuỗi định dạng khi chạy, nhưng nó thường có một lợi thế là không yêu cầu phân bổ bộ nhớ quá mức (điều này phụ thuộc vào trình biên dịch và thời gian chạy của bạn). Điều đó nói rằng, trừ khi hiệu suất là mục tiêu cuối cùng duy nhất của bạn và bạn đang ở trong con đường quan trọng thì bạn nên thực sự ủng hộ các phương pháp an toàn hơn (chậm hơn).

Có một bài viết rất hay được viết ở đây bởi Herb Sutter " Các chuỗi Định dạng của Manor Farm " người đi vào nhiều chi tiết của việc thực hiện và định dạng chuỗi như sscanflexical_castvà loại thứ đã được làm cho chúng chạy chậm hoặc nhanh chóng. Đây là loại tương tự, có thể là loại những thứ sẽ ảnh hưởng đến hiệu suất giữa phong cách C và IO kiểu C. Sự khác biệt chính với các trình định dạng có xu hướng là loại an toàn và số lượng phân bổ bộ nhớ.


19

Tôi vừa trải qua một buổi tối làm việc về một vấn đề trên UVa Online (Factovisors, một vấn đề rất thú vị, hãy xem thử):

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&carget=35&page=show_probols&probols=1080

Tôi đã nhận được TLE (vượt quá giới hạn thời gian) trong bài nộp của mình. Trên các trang web giải quyết vấn đề trực tuyến này, bạn có giới hạn thời gian 2-3 giây để xử lý hàng ngàn trường hợp thử nghiệm được sử dụng để đánh giá giải pháp của bạn. Đối với các vấn đề tính toán chuyên sâu như thế này, cứ sau mỗi micro giây.

Tôi đã sử dụng thuật toán được đề xuất (đọc về các diễn đàn thảo luận cho trang web), nhưng vẫn nhận được TLE.

Tôi đã thay đổi chỉ "cin >> n >> m" thành "scanf ("% d% d ", & n, & m)" và một vài "cout" nhỏ thành "printfs" và TLE của tôi biến thành "Được chấp nhận"!

Vì vậy, vâng, nó có thể tạo ra một sự khác biệt lớn, đặc biệt là khi giới hạn thời gian là ngắn.


Đồng ý. Điều tương tự cũng xảy ra với tôi trong vấn đề Thẩm phán trực tuyến UVA: Bạn thân quân đội uva.onlinejudge.org/iêu
Mohamed El-Nakib

6

Nếu bạn quan tâm đến cả hiệu suất và định dạng chuỗi, hãy xem FastFormat của Matthew Wilson thư viện .

chỉnh sửa - liên kết đến ấn phẩm accu trên thư viện đó: http://accu.org/index.php/journals/1539


Hoan toan Đông y. Nhưng bạn cần lưu ý rằng FastFormat chỉ dành cho đầu ra. Nó không có cơ sở đầu vào / đọc. (Chưa, dù sao)
dcw

Thật không may liên kết đó dường như đã chết. Đây là bản sao của Wayback Machine: web.archive.org/web/20081222164527/http://fastformat.org
nibot

2

Có các triển khai stdio ( libio ) thực hiện FILE * dưới dạng streambuf của C ++ và fprintf như một trình phân tích cú pháp định dạng thời gian chạy. IOux không cần phân tích cú pháp định dạng thời gian chạy, tất cả đều được thực hiện vào thời gian biên dịch. Vì vậy, với các phụ trợ được chia sẻ, thật hợp lý khi hy vọng rằng iostreams sẽ nhanh hơn khi chạy.


Tôi không nghĩ vậy. Tôi nghĩ libc của GNU là C thuần túy và lắp ráp.
Chris Lutz

2

Có iostream chậm hơn cstdio.
Có, bạn có thể không nên sử dụng cstdio nếu bạn đang phát triển trong C ++.
Phải nói rằng, thậm chí còn có cách nhanh hơn để nhận I / O so với scanf nếu bạn không quan tâm đến định dạng, loại an toàn, blah, blah, blah ...

Chẳng hạn, đây là một thói quen tùy chỉnh để lấy số từ 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 () là không chuẩn và có sẵn cho gcc không phải là studio trực quan
Mohamed El-Nakib

1

Vấn đề là cincó rất nhiều chi phí liên quan vì nó cung cấp cho bạn một lớp trừu tượng bên trên scanf()các cuộc gọi. Bạn không nên sử dụng scanf()trên cinnếu bạn đang viết phần mềm C ++ bởi vì đó là muốn cinlà cho. Nếu bạn muốn hiệu suất, có lẽ bạn sẽ không viết I / O bằng C ++.


2
cinthực sự "trừu tượng" (tại thời gian chạy) hơn scanf? Tôi không nghĩ vậy ... scanfphải diễn giải chuỗi định dạng trong thời gian chạy, trong khi iostreamđịnh dạng biết tại thời gian biên dịch.
nibot

1
@nibot: Loại được biết tại thời gian biên dịch nhưng không phải là định dạng . Việc đầu vào có được dự kiến ​​là thập lục phân hay không, ví dụ hoàn toàn phụ thuộc vào cách std::istreamcấu hình trong thời gian chạy (thông qua các thao tác I / O hoặc bằng cách đặt cờ trên istreamchính đối tượng). Mặt khác, một FILE*đối tượng không có trạng thái như vậy, vì vậy một cuộc gọi scanftrong vấn đề này ổn định hơn nhiều.
dreamlax

1

Các báo cáo cincoutsử dụng chung dường như chậm hơn scanfprintf trong C ++, nhưng thực sự chúng là NHANH CHÓNG!

Vấn đề là: Trong C ++, bất cứ khi nào bạn sử dụng cincout, một quá trình đồng bộ hóa diễn ra theo mặc định sẽ đảm bảo rằng nếu bạn sử dụng cả hai scanfcintrong chương trình của mình, thì cả hai đều hoạt động đồng bộ với nhau. Quá trình đồng bộ này mất thời gian. Do đó cincoutxuất hiện để chậm hơn.

Tuy nhiên, nếu quá trình đồng bộ hóa được đặt thành không xảy ra, cinsẽ nhanh hơn scanf.

Để bỏ qua quá trình đồng bộ hóa, hãy bao gồm đoạn mã sau trong chương trình của bạn ngay khi bắt đầu main():

std::ios::sync_with_stdio(false);

Ghé thăm trang web này để biết thêm thông tin.


+1 cho lời giải thích của bạn về đồng bộ hóa. Tôi vừa tắt đồng bộ hóa và sử dụng cả scanf và cin trong một số mã . bây giờ tôi biết những gì đã xảy ra với nó. cảm ơn bạn!
Dariush

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ó một lỗi ở cuối tập tin, nhưng mã C này nhanh hơn đáng kể so với phiên bản C ++ nhanh hơn.

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 ++ ban đầu mất 30 giây, mã C mất 2 giây.


-1

Tất nhiên thật vô lý khi sử dụng cstdio trên iostream. Ít nhất là khi bạn phát triển phần mềm (nếu bạn đã sử dụng c ++ trên c, thì hãy tìm mọi cách và sử dụng lợi ích của nó thay vì chỉ chịu những bất lợi của nó).

Nhưng trong thẩm phán trực tuyến, bạn không phát triển phần mềm, bạn đang tạo một chương trình có thể thực hiện những điều mà phần mềm của Microsoft mất 60 giây để đạt được trong 3 giây !!!

Vì vậy, trong trường hợp này, quy tắc vàng sẽ diễn ra (tất nhiên nếu bạn không gặp rắc rối hơn nữa bằng cách sử dụng java)

  • Sử dụng c ++ và sử dụng tất cả sức mạnh của nó (và độ nặng / chậm) để giải quyết vấn đề
  • Nếu bạn bị giới hạn thời gian, sau đó thay đổi các cins và cout cho printfs và scanfs (nếu bạn bị làm hỏng bằng cách sử dụng chuỗi lớp, hãy in như thế này: printf (% s, mystr.c_str ());
  • Nếu bạn vẫn bị giới hạn thời gian, thì hãy thử thực hiện một số tối ưu hóa rõ ràng (như tránh quá nhiều nhúng cho / while / dowhiles hoặc các hàm đệ quy). Ngoài ra, hãy đảm bảo vượt qua các đối tượng tham chiếu quá lớn ...
  • Nếu bạn vẫn bị giới hạn thời gian, thì hãy thử thay đổi std :: vectơ và thiết lập cho mảng c.
  • Nếu bạn vẫn bị giới hạn thời gian, thì hãy chuyển sang vấn đề tiếp theo ...

-2

Ngay cả khi scanfnhanh hơn cin, nó cũng không thành vấn đề. Phần lớn thời gian, bạn sẽ đọc từ ổ cứng hoặc bàn phím. Lấy dữ liệu thô vào ứng dụng của bạn mất tự độ lớn hơn thời gian hơn phải mất scanfhoặc cinđể xử lý nó.


IPC qua đường ống thì sao? Bạn có nghĩ rằng có thể có một hiệu suất đáng chú ý ở đó?
dreamlax

Ngay cả với IPC thông qua các đường ống, nhiều thời gian hơn vào và ra khỏi kernel hơn là chỉ phân tích cú pháp bằng scanf / cin.
Jay Conrod

8
Tôi đã làm các bài kiểm tra trong lĩnh vực này, và chắc chắn hiệu suất cout & cin hút. Mặc dù đối với đầu vào của người dùng, nó không đáng kể, nhưng chắc chắn không phải vậy đối với những thứ mà hiệu suất hoạt động. Mặc dù vậy, khung c ++ khác tồn tại nhanh hơn.
Johannes Schaub - litb

Vấn đề là iostream chậm hơn so với hdd. Vâng, nó hút rất nhiều.
polkovnikov.ph
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.