การใช้ scanf () ในโปรแกรม C ++ จะเร็วกว่าการใช้ cin?


126

ฉันไม่รู้ว่าเป็นเรื่องจริงหรือเปล่า แต่เมื่อฉันอ่านคำถามที่พบบ่อยเกี่ยวกับปัญหาหนึ่งในการจัดหาเว็บไซต์ฉันพบบางอย่างที่กระตุ้นความสนใจของฉัน:

ตรวจสอบวิธีอินพุต / เอาต์พุตของคุณ ใน C ++ การใช้ cin และ cout ช้าเกินไป ใช้สิ่งเหล่านี้และคุณจะรับประกันว่าจะไม่สามารถแก้ไขปัญหาใด ๆ ด้วยอินพุตหรือเอาต์พุตในปริมาณที่เหมาะสม ใช้ printf และ scanf แทน

ใครช่วยชี้แจงเรื่องนี้ได้ไหม การใช้scanf ()ในโปรแกรม C ++ เร็วกว่าการใช้cin >> บางอย่างหรือไม่? ถ้าใช่การใช้งานในโปรแกรม C ++ เป็นวิธีปฏิบัติที่ดีหรือไม่? ฉันคิดว่ามันเป็นภาษา C เฉพาะแม้ว่าฉันจะเพิ่งเรียนรู้ C ++ ...


14
ฉันเดา: โปรแกรมเมอร์ที่ไม่ดีโทษว่าไลบรารีมาตรฐานมีประสิทธิภาพต่ำ ชอบเสียงร้อง "ฉันคิดว่าฉันพบข้อบกพร่องใน GCC" ที่มีอารมณ์ขันอยู่เสมอ
John Kugelman

11
@eclipse: ปัญหา ACM ที่ฉันใช้ในการแข่งขันมีอินพุต / เอาท์พุตจำนวนมากและโปรแกรมของคุณต้องแก้ปัญหาภายใน 60 วินาที ... มันกลายเป็นปัญหาจริงที่นี่
mpen

19
--- ที่กล่าวว่าหากคุณจำเป็นต้องพึ่งพา scanf () สำหรับการเพิ่มประสิทธิภาพพิเศษนั้นคุณจะประสบปัญหาในทางที่ผิด :)
mpen

4
เช่นเดียวกับการสังเกต - ฉันเล่นกับมันและในปัญหาที่ 2 (PRIME1) - โดยใช้อัลกอริทึมเดียวกันทั้งสองครั้งครั้งหนึ่งใช้ cin / cout และอีกครั้งกับ scanf / printf และเวอร์ชันแรกเร็วกว่าครั้งที่สอง (แต่ ใกล้พอที่จะไม่เกี่ยวข้องทางสถิติ) นี่เป็นหนึ่งในปัญหาที่ถูกระบุว่าเป็นอินพุต / เอาต์พุตที่เข้มข้นและวิธีการอินพุต / เอาต์พุตไม่มีความแตกต่างทางสถิติใด ๆ
คราส

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 ชี้ให้เห็นด้านล่างว่าความแตกต่างของความเร็วส่วนใหญ่เกิดจากฟังก์ชัน iostream I / O ที่รักษาการซิงโครไนซ์กับฟังก์ชัน CI / 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' อาจทำให้การดำเนินการช้าลง
Krishna Mohan

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 ++ ที่เกี่ยวข้อง


เพิ่งตรวจสอบบรรทัดด้านบน (std :: ios :: sync_with_stdio (false);) และมันทำให้ iostream เร็วเกือบเท่า cstdio
gabrielhidasy

ยังใช้ cin.tie (static_cast <ostream *> (0)); เพื่อประสิทธิภาพที่ดีขึ้น
Mohamed El-Nakib

42

scanf อาจจะค่อนข้างเร็วกว่าการใช้สตรีม แม้ว่าสตรีมจะให้ความปลอดภัยประเภทต่างๆมากมายและไม่ต้องแยกวิเคราะห์สตริงรูปแบบที่รันไทม์ แต่ก็มักจะมีข้อได้เปรียบที่ไม่ต้องการการจัดสรรหน่วยความจำมากเกินไป (ขึ้นอยู่กับคอมไพเลอร์และรันไทม์ของคุณ) ที่กล่าวว่าเว้นแต่ว่าประสิทธิภาพจะเป็นเป้าหมายสุดท้ายเพียงอย่างเดียวของคุณและคุณอยู่ในเส้นทางวิกฤตคุณควรเลือกวิธีที่ปลอดภัยกว่า (ช้ากว่า)

มีบทความที่น่าอร่อยมากที่เขียนโดย Herb Sutter " The String Formatters of Manor Farm " ซึ่งให้รายละเอียดมากมายเกี่ยวกับประสิทธิภาพของการจัดรูปแบบสตริงเช่นsscanfและlexical_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 ของฉันก็เปลี่ยนเป็น "ยอมรับแล้ว"!

ใช่มันสามารถสร้างความแตกต่างได้มากโดยเฉพาะอย่างยิ่งเมื่อเวลาที่ จำกัด สั้น


ตกลง. เกิดขึ้นกับฉันใน UVA Online Judge problem: Army Buddies uva.onlinejudge.org/…
Mohamed El-Nakib

6

หากคุณสนใจทั้งประสิทธิภาพและการจัดรูปแบบสตริงลองดูที่ไลบรารีFastFormat ของ Matthew Wilson

แก้ไข - ลิงก์ไปยังสิ่งพิมพ์ของ Accu ในไลบรารีนั้น: http://accu.org/index.php/journals/1539


เห็นด้วยอย่างยิ่ง แต่คุณต้องทราบว่า FastFormat มีไว้สำหรับเอาต์พุตเท่านั้น ไม่มีสิ่งอำนวยความสะดวกในการป้อนข้อมูล / อ่าน (ยังไม่ได้)
dcw

น่าเสียดายที่ลิงค์นั้นดูเหมือนจะตายไปแล้ว นี่คือสำเนา Wayback Machine: web.archive.org/web/20081222164527/http://fastformat.org
nibot

2

มีการใช้งาน stdio ( libio ) ซึ่งใช้ FILE * เป็น C ++ streambuf และ fprintf เป็นตัวแยกวิเคราะห์รูปแบบรันไทม์ IOstreams ไม่จำเป็นต้องแยกวิเคราะห์รูปแบบรันไทม์ซึ่งทำได้ทั้งหมดในเวลาคอมไพล์ ดังนั้นด้วยแบ็กเอนด์ที่ใช้ร่วมกันจึงสมเหตุสมผลที่จะคาดหวังว่า iostreams จะเร็วกว่าในขณะรันไทม์


ฉันไม่คิดอย่างนั้น ฉันคิดว่า libc ของ GNU เป็น C และแอสเซมบลีที่บริสุทธิ์
Chris Lutz

2

ใช่ iostream ช้ากว่า cstdio
ใช่คุณอาจไม่ควรใช้ cstdio หากคุณกำลังพัฒนาใน C ++
ต้องบอกว่ามีวิธีที่เร็วกว่าในการรับ I / O มากกว่า scanf หากคุณไม่สนใจเกี่ยวกับการจัดรูปแบบประเภทความปลอดภัย blah blah blah ...

ตัวอย่างเช่นนี่เป็นกิจวัตรที่กำหนดเองเพื่อรับหมายเลขจาก 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 not visual studio
Mohamed El-Nakib

2

ข้อความcinและcoutการใช้งานทั่วไปดูเหมือนจะช้ากว่าscanfและprintfใน C ++ แต่จริงๆแล้วมันเร็วกว่า!

สิ่งนี้คือ: ใน C ++ เมื่อใดก็ตามที่คุณใช้cinและcoutกระบวนการซิงโครไนซ์จะเกิดขึ้นตามค่าเริ่มต้นซึ่งทำให้แน่ใจว่าหากคุณใช้ทั้งสองอย่างscanfและcinในโปรแกรมของคุณทั้งสองจะทำงานโดยซิงค์กัน กระบวนการซิงค์นี้ต้องใช้เวลา ดังนั้นcinและcoutดูเหมือนจะช้าลง

แต่ถ้าการประสานการตั้งค่าเพื่อไม่ให้เกิดขึ้นเร็วกว่าcinscanf

หากต้องการข้ามขั้นตอนการซิงค์ให้ใส่ข้อมูลโค้ดต่อไปนี้ในโปรแกรมของคุณที่จุดเริ่มต้นของmain():

std::ios::sync_with_stdio(false);

เยี่ยมชมไซต์นี้เพื่อดูข้อมูลเพิ่มเติม


+1 สำหรับคำอธิบายของคุณเกี่ยวกับการซิงโครไนซ์ ผมได้เปิดเพียงซิงค์ออกและใช้ทั้ง scanf และ CIN ในรหัสบางอย่าง ตอนนี้ฉันรู้แล้วว่าเกิดอะไรขึ้น ขอบคุณ!
Dariush

1

ปัญหาคือcinมีค่าใช้จ่ายจำนวนมากที่เกี่ยวข้องเพราะมันทำให้คุณมีเลเยอร์นามธรรมเหนือการscanf()โทร คุณไม่ควรใช้scanf()มากกว่าcinถ้าคุณเขียนซอฟแวร์ c ++ เพราะเห็นว่าเป็นความต้องการของคุณcinเป็น หากคุณต้องการประสิทธิภาพคุณอาจจะไม่ได้เขียน I / O ใน C ++


2
เป็นcinจริงมากขึ้น "นามธรรม" (ที่รันไทม์) มากกว่าscanf? ฉันไม่คิดอย่างนั้น ... scanfต้องตีความสตริงรูปแบบที่รันไทม์ในขณะที่iostreamรู้รูปแบบในเวลาคอมไพล์
nibot

1
@nibot: ในประเภทเป็นที่รู้จักกันที่รวบรวมเวลา แต่ไม่ใช่รูปแบบ การที่อินพุตคาดว่าจะเป็นเลขฐานสิบหกหรือไม่เช่นนั้นขึ้นอยู่กับวิธีstd::istreamกำหนดค่าในรันไทม์ (ผ่านตัวปรับแต่ง I / O หรือโดยการตั้งค่าแฟล็กบนistreamอ็อบเจ็กต์เอง) FILE*วัตถุบนมืออื่น ๆ ที่ไม่เคยมีใครรัฐดังกล่าวจึงเรียกร้องให้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

แน่นอนว่ามันไร้สาระที่จะใช้ cstdio กับ iostream อย่างน้อยที่สุดเมื่อคุณพัฒนาซอฟต์แวร์ (หากคุณใช้ c ++ มากกว่า c อยู่แล้วให้ทำทุกวิถีทางและใช้ประโยชน์ของมันแทนที่จะต้องทนทุกข์ทรมานจากข้อเสียเท่านั้น)

แต่ในการตัดสินออนไลน์คุณไม่ได้พัฒนาซอฟต์แวร์คุณกำลังสร้างโปรแกรมที่ควรจะทำสิ่งต่างๆซอฟต์แวร์ Microsoft ใช้เวลา 60 วินาทีเพื่อให้บรรลุใน 3 วินาที !!!

ดังนั้นในกรณีนี้กฎทองก็เป็นเช่นนั้น (แน่นอนว่าถ้าคุณไม่ได้รับปัญหามากขึ้นโดยใช้ java)

  • ใช้ c ++ และใช้พลังทั้งหมด (และความหนัก / ความช้า) ในการแก้ปัญหา
  • หากคุณมีเวลา จำกัด ให้เปลี่ยน cins และ couts สำหรับ printfs และ scanfs (ถ้าคุณเมาโดยใช้ class string ให้พิมพ์ดังนี้ printf (% s, mystr.c_str ());
  • หากคุณยังมีเวลา จำกัด ให้พยายามเพิ่มประสิทธิภาพที่ชัดเจน (เช่นหลีกเลี่ยงการฝังตัวมากเกินไปสำหรับ / while / dowhiles หรือฟังก์ชันวนซ้ำ) ตรวจสอบให้แน่ใจว่าได้ส่งผ่านวัตถุอ้างอิงที่ใหญ่เกินไป ...
  • หากคุณยังมีเวลา จำกัด ลองเปลี่ยน std :: vectors และชุดสำหรับ c-arrays
  • หากคุณยังมีเวลา จำกัด ให้ไปที่ปัญหาต่อไป ...

-2

แม้ว่าscanfจะเร็วกว่าcinแต่ก็ไม่สำคัญ ส่วนใหญ่แล้วคุณจะอ่านจากฮาร์ดไดรฟ์หรือแป้นพิมพ์ การรับข้อมูลดิบเข้าสู่แอปพลิเคชันของคุณต้องใช้เวลามากกว่าที่จะใช้scanfหรือcinในการประมวลผล


แล้ว IPC ผ่านท่อล่ะ? คุณคิดว่าอาจมีผลงานที่เห็นได้ชัดเจนที่นั่นหรือไม่?
dreamlax

แม้จะใช้ IPC ผ่านท่อ แต่ก็ยังใช้เวลาเข้าและออกจากเคอร์เนลได้มากกว่าการแยกวิเคราะห์ด้วย scanf / cin
Jay Conrod

8
ฉันทำการทดสอบในพื้นที่นี้และแน่นอนว่า cout & cin ดูดประสิทธิภาพ แม้ว่าการป้อนข้อมูลของผู้ใช้จะไม่สำคัญ แต่ก็ไม่เป็นเช่นนั้นสำหรับสิ่งที่ประสิทธิภาพมีความสำคัญ มีกรอบ c ++ อื่น ๆ ที่เร็วกว่า
Johannes Schaub - litb

ปัญหาคือว่าiostream เป็นช้ากว่าฮาร์ดดิสก์ ใช่มันห่วยมาก
polkovnikov.ph
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.