Menggunakan scanf () dalam program C ++ lebih cepat daripada menggunakan cin?

126

Saya tidak tahu apakah ini benar, tetapi ketika saya membaca FAQ tentang salah satu masalah dalam menyediakan situs, saya menemukan sesuatu, yang menarik perhatian saya:

Periksa metode input / output Anda. Di C ++, menggunakan cin dan cout terlalu lambat. Gunakan ini, dan Anda akan menjamin tidak dapat menyelesaikan masalah dengan jumlah input atau output yang layak. Gunakan printf dan scanf sebagai gantinya.

Bisakah seseorang tolong klarifikasi ini? Apakah benar-benar menggunakan scanf () dalam program C ++ lebih cepat daripada menggunakan cin >> sesuatu ? Jika ya, apakah itu praktik yang baik untuk menggunakannya dalam program C ++? Saya pikir itu spesifik C, walaupun saya baru belajar C ++ ...

zeroDivisible
sumber
14
Dugaan saya: programmer buruk menyalahkan perpustakaan standar untuk kinerja yang buruk. Agak suka selalu lucu "Saya pikir saya menemukan bug di GCC" menangis.
John Kugelman
11
@eclipse: masalah ACM yang telah saya kerjakan untuk kompetisi memiliki sejumlah besar input / output dan program Anda harus menyelesaikan pertanyaan dalam waktu kurang dari 60 detik ... itu menjadi masalah nyata di sini.
mpen
19
--- yang mengatakan, jika Anda perlu bergantung pada scanf () untuk itu meningkatkan kinerja ekstra, Anda akan tentang masalah dengan cara yang salah :)
mpen
4
Sama seperti pengamatan - saya bermain-main dengannya, dan pada masalah ke-2 (PRIME1) - menggunakan algoritma yang sama, dua kali, sekali menggunakan cin / cout dan sekali dengan scanf / printf dan versi pertama lebih cepat daripada yang kedua (tetapi cukup dekat sehingga tidak relevan secara statistik). Ini adalah salah satu masalah yang ditandai sebagai input / output intensif, dan metode input / output tidak membuat perbedaan statistik apa pun.
Eclipse
4
@Eclipse - terima kasih atas informasi tentang pengujian kedua metode. Aku sedih meskipun - saya mencoba untuk menyalahkan cin dan cout, tapi sekarang aku tahu bahwa algoritma saya menyebalkan :)
zeroDivisible

Jawaban:

209

Berikut ini adalah tes cepat untuk kasing sederhana: program untuk membaca daftar angka dari input standar dan XOR semua angka.

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

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

Hasil

Menggunakan program ketiga, saya membuat file teks yang berisi 33.280.276 angka acak. Waktu eksekusi adalah:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

Mengubah pengaturan optimisasi kompiler sepertinya tidak banyak mengubah hasil.

Jadi: memang ada perbedaan kecepatan.


EDIT: Pengguna clyfish menunjukkan di bawah ini bahwa perbedaan kecepatan sebagian besar disebabkan oleh fungsi Iostream I / O yang menjaga sinkronisasi dengan fungsi CI / O. Kami dapat menonaktifkannya dengan panggilan ke 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;
}

Hasil baru:

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

C ++ iostream menang! Ternyata sinkronisasi / pembilasan internal inilah yang biasanya memperlambat iostream i / o. Jika kita tidak mencampur stdio dan iostream, kita dapat mematikannya, dan kemudian iostream tercepat.

Kode: https://gist.github.com/3845568

nibot
sumber
6
Saya pikir penggunaan 'endl' dapat memperlambat eksekusi.
Krishna Mohan
2
Penggunaan std :: endl tidak ada dalam loop.
nibot
Tidak ada bedanya dengan sinkronisasi aktif atau nonaktif. Salahkan libc ++ untuk itu. Ini hanya meningkatkan libstdc ++
iBug
Apakah Anda pikir akan ada perbedaan antara <cstdio> dan <stdio.h> ??
Chandrahas Aroori
iostreamhilang ketika Anda mem-parsing lebih dari satu bilangan bulat dalam satu scanfpanggilan.
Maxim Egorushkin
68

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

Kinerja cin/ coutbisa lambat karena mereka harus tetap sinkron dengan pustaka C yang mendasarinya. Ini penting jika C IO dan C ++ IO akan digunakan.

Namun, jika Anda hanya akan menggunakan C ++ IO, maka cukup gunakan baris di bawah ini sebelum operasi IO.

std::ios::sync_with_stdio(false);

Untuk info lebih lanjut tentang ini, lihat dokumen libstdc ++ yang sesuai .

clyfish
sumber
Baru saja memeriksa baris di atas (std :: ios :: sync_with_stdio (false);) Dan itu benar-benar membuat iostream hampir secepat cstdio
gabrielhidasy
juga menggunakan cin.tie (static_cast <ostream *> (0)); untuk kinerja yang lebih baik
Mohamed El-Nakib
42

Mungkin scanf agak lebih cepat daripada menggunakan stream. Walaupun stream menyediakan banyak tipe keamanan, dan tidak perlu mengurai string format saat runtime, biasanya stream memiliki keuntungan karena tidak memerlukan alokasi memori yang berlebihan (ini tergantung pada kompiler dan runtime Anda). Yang mengatakan, kecuali kinerja adalah satu-satunya tujuan akhir Anda dan Anda berada di jalur kritis maka Anda harus benar-benar mendukung metode yang lebih aman (lebih lambat).

Ada artikel yang sangat lezat yang ditulis di sini oleh Herb Sutter " The String Formatters of Manor Farm " yang membahas banyak detail tentang kinerja pembuat string sscanfdan lexical_castdan hal-hal seperti apa yang membuatnya berjalan lambat atau cepat. Ini semacam analog, mungkin dengan hal-hal yang akan mempengaruhi kinerja antara gaya C IO dan gaya C ++. Perbedaan utama dengan format cenderung pada keamanan jenis dan jumlah alokasi memori.

INFORMASI 1800
sumber
19

Saya baru saja menghabiskan malam mengerjakan masalah di UVa Online (Factovisors, masalah yang sangat menarik, periksa):

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

Saya mendapatkan TLE (batas waktu terlampaui) pada kiriman saya. Pada situs hakim online penyelesaian masalah ini, Anda memiliki batas waktu sekitar 2-3 detik untuk menangani ribuan kasus uji yang berpotensi digunakan untuk mengevaluasi solusi Anda. Untuk masalah intensif komputasi seperti ini, setiap mikrodetik penting.

Saya menggunakan algoritma yang disarankan (baca di forum diskusi untuk situs), tetapi masih mendapatkan TLE.

Saya mengubah "cin >> n >> m" menjadi "scanf ("% d% d ", & n, & m)" dan beberapa "couts" kecil menjadi "printfs", dan TLE saya berubah menjadi "Diterima"!

Jadi, ya, itu bisa membuat perbedaan besar, terutama ketika batas waktu singkat.

Bogatyr
sumber
Setuju. Hal yang sama terjadi pada saya dalam masalah Hakim UVA Online: Army Buddies uva.onlinejudge.org/…
Mohamed El-Nakib
2

Ada implementasi stdio ( libio ) yang mengimplementasikan FILE * sebagai C ++ streambuf, dan fprintf sebagai pengurai format runtime. IOstreams tidak perlu parsing format runtime, itu semua dilakukan pada waktu kompilasi. Jadi, dengan backend dibagikan, masuk akal untuk berharap bahwa iostreams lebih cepat saat runtime.

MSalters
sumber
Saya kira tidak. Saya pikir libc GNU adalah C murni dan perakitan.
Chris Lutz
2

Ya iostream lebih lambat dari cstdio.
Ya Anda mungkin tidak boleh menggunakan cstdio jika Anda berkembang di C ++.
Karena itu, bahkan ada cara yang lebih cepat untuk mendapatkan I / O daripada memindai jika Anda tidak peduli tentang pemformatan, keamanan jenis, bla, bla, bla ...

Misalnya ini adalah kebiasaan khusus untuk mendapatkan nomor dari 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;
}
pedro.lupin
sumber
1
getchar_unlocked () adalah non-standar, dan tersedia untuk gcc bukan visual studio
Mohamed El-Nakib
1

Masalahnya adalah cinada banyak overhead yang terlibat karena memberi Anda lapisan abstraksi di atas scanf()panggilan. Anda tidak boleh menggunakan scanf()lebih dari cinjika Anda menulis perangkat lunak C ++ karena yang diinginkan cinadalah untuk. Jika Anda menginginkan kinerja, Anda mungkin tidak akan menulis I / O dalam C ++.

dreamlax
sumber
2
Apakah cinbenar-benar lebih "abstrak" (saat runtime) daripada scanf? Saya tidak berpikir begitu ... scanfharus menafsirkan string format saat runtime, sedangkan yang iostreamtahu format pada waktu kompilasi.
nibot
1
@nibot: Jenis ini diketahui pada waktu kompilasi tetapi tidak dalam format . Apakah input diharapkan hexadecimal atau tidak misalnya, sepenuhnya tergantung pada bagaimana std::istreamdikonfigurasi pada saat runtime (melalui I / O manipulator atau dengan mengatur flag pada istreamobjek itu sendiri). Sebuah FILE*objek di sisi lain tidak memiliki negara tersebut, sehingga panggilan untuk scanfdalam hal ini jauh lebih stabil.
dreamlax
1

Pernyataan cindan coutdalam penggunaan umum tampaknya lebih lambat daripada scanfdan printfdi C ++, tetapi sebenarnya mereka LEBIH CEPAT!

Masalahnya adalah: Di C ++, setiap kali Anda menggunakan cindan cout, proses sinkronisasi dilakukan secara default yang memastikan bahwa jika Anda menggunakan keduanya scanfdan cindalam program Anda, maka keduanya bekerja sinkron satu sama lain. Proses sinkronisasi ini membutuhkan waktu. Karenanya cindan coutMUNCUL menjadi lebih lambat.

Namun, jika proses sinkronisasi diatur agar tidak terjadi, cinlebih cepat dari scanf.

Untuk melewati proses sinkronisasi, sertakan snipet kode berikut dalam program Anda tepat di awal main():

std::ios::sync_with_stdio(false);

Kunjungi situs ini untuk informasi lebih lanjut.

Prasoon Varshney
sumber
+1 untuk penjelasan Anda tentang sinkronisasi. Saya baru saja mematikan sinkronisasi dan menggunakan scanf dan cin dalam beberapa kode . sekarang saya tahu apa yang salah dengan itu. Terima kasih!
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;
}

Ada bug di akhir file, tetapi kode C ini secara dramatis lebih cepat daripada versi C ++ yang lebih cepat.

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 ++ asli mengambil 30 detik kode C mengambil 2 detik.

hexec
sumber
-1

Tentu saja konyol menggunakan cstdio di atas iostream. Setidaknya ketika Anda mengembangkan perangkat lunak (jika Anda sudah menggunakan c ++ lebih dari c, maka lanjutkan dan gunakan manfaatnya alih-alih hanya menderita dari kelemahannya).

Tetapi pada juri online Anda tidak sedang mengembangkan perangkat lunak, Anda sedang membuat sebuah program yang seharusnya dapat melakukan hal-hal yang dibutuhkan perangkat lunak Microsoft dalam 60 detik dalam 3 detik !!!

Jadi, dalam hal ini, aturan emasnya seperti (tentu saja jika Anda tidak mendapatkan lebih banyak masalah dengan menggunakan java)

  • Gunakan c ++ dan gunakan semua kekuatannya (dan bobot / kelambatan) untuk menyelesaikan masalah
  • Jika waktu Anda terbatas, maka ubah cins dan couts untuk printfs dan scanfs (jika Anda kacau dengan menggunakan string kelas, cetak seperti ini: printf (% s, mystr.c_str ());
  • Jika waktu Anda masih terbatas, cobalah membuat beberapa optimasi yang jelas (seperti menghindari terlalu banyak embedded untuk / while / dowhiles atau fungsi rekursif). Pastikan juga untuk melewati referensi objek yang terlalu besar ...
  • Jika waktu Anda masih terbatas, coba ubah std :: vektor dan set untuk c-array.
  • Jika waktu Anda masih terbatas, lanjutkan ke masalah berikutnya ...
Carlos Pacheco
sumber
-2

Bahkan jika scanflebih cepat dari cinitu, itu tidak masalah. Sebagian besar waktu, Anda akan membaca dari hard drive atau keyboard. Memasukkan data mentah ke dalam aplikasi Anda membutuhkan pesanan lebih banyak waktu daripada yang dibutuhkan scanfatau cinuntuk memprosesnya.

Jay Conrod
sumber
Bagaimana dengan IPC melalui pipa? Apakah Anda pikir mungkin ada kinerja yang nyata di sana?
dreamlax
Bahkan dengan IPC melalui pipa, lebih banyak waktu dihabiskan untuk masuk dan keluar dari kernel daripada hanya menguraikannya dengan scanf / cin.
Jay Conrod
8
Saya melakukan tes di bidang ini, dan tentu saja menghisap kinerja. Sedangkan untuk input pengguna dapat diabaikan, tentu saja tidak demikian halnya untuk hal-hal yang menyangkut kinerja. Kerangka c ++ lainnya ada yang lebih cepat.
Johannes Schaub - litb
Masalahnya adalah bahwa iostream adalah lebih lambat dari hdd. Ya, itu sangat menyebalkan.
polkovnikov.ph