C ++ programlarında scanf () kullanmak cin kullanmaktan daha hızlı mı?


126

Bunun doğru olup olmadığını bilmiyorum, ancak sorun sağlayan sitelerden birinde SSS'yi okurken dikkatimi çeken bir şey buldum:

Giriş / çıkış yöntemlerinizi kontrol edin. C ++ 'da cin ve cout kullanmak çok yavaştır. Bunları kullanın ve herhangi bir sorunu makul miktarda girdi veya çıktıyla çözemeyeceğinizi garanti edeceksiniz. Bunun yerine printf ve scanf kullanın.

Lütfen birisi bunu açıklayabilir mi? Gerçekten kullanıyor scanf () hızlı kullanmaktan daha C ++ programlarında cin >> şey ? Evetse, C ++ programlarında kullanmak iyi bir uygulama mı? C'ye özgü olduğunu düşündüm, ancak sadece C ++ öğreniyor olsam da ...


14
Tahminim: kötü programcı, düşük performans için standart kitaplıkları suçluyor. Her zaman komik olan "GCC'de bir hata bulduğumu düşünüyorum" çığlığı gibi.
John Kugelman

11
@eclipse: Yarışmalar için üzerinde çalıştığım ACM problemleri önemli miktarda girdi / çıktı içeriyor ve programınız soruları 60 saniye gibi bir sürede çözmeli ... burada gerçek bir sorun haline geliyor.
mpen

19
--- bununla birlikte, bu ekstra performans artışı için scanf () 'e güvenmeniz gerekiyorsa, sorunu yanlış şekilde
çözüyorsunuz

4
Tıpkı bir gözlem gibi - onunla ve 2. problemlerde (PRIME1) oynadım - her iki seferde de aynı algoritmayı kullanarak, bir kez cin / cout ve bir kez scanf / printf kullanarak ve ilk sürüm ikinciden daha hızlıydı (ancak istatistiksel olarak alakasız olacak kadar yakın). Bu, girdi / çıktı yoğun olarak işaretlenen sorunlardan biridir ve girdi / çıktı yöntemi hiçbir şekilde istatistiksel bir fark yaratmadı.
Eclipse

4
@Eclipse - her iki yöntemi de test etme hakkındaki bilgiler için teşekkürler. Yine de üzgünüm - cin ve
cout'u

Yanıtlar:


209

İşte basit bir durumun hızlı bir testi: standart girişten sayıların bir listesini okuyan bir program ve tüm sayıları XOR.

iostream sürümü:

#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 sürümü:

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

Sonuçlar

Üçüncü bir program kullanarak 33,280,276 rastgele sayı içeren bir metin dosyası oluşturdum. Uygulama süreleri:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

Derleyicinin optimizasyon ayarlarını değiştirmek, sonuçları pek değiştirmemiş gibi görünüyordu.

Bu nedenle: gerçekten bir hız farkı var.


DÜZENLEME: Kullanıcı clyfish aşağıda hız farkının büyük ölçüde CI / O işlevleriyle senkronizasyonu sağlayan iostream I / O işlevlerinden kaynaklandığını belirtiyor. Bunu şu numarayı arayarak kapatabiliriz 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;
}

Yeni sonuçlar:

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

C ++ iostream kazandı! Bu dahili senkronizasyon / yıkama işleminin normalde iostream i / o'yu yavaşlatan şey olduğu ortaya çıktı. Standart ve iostream'i karıştırmıyorsak, onu kapatabiliriz ve sonra iostream en hızlısıdır.

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


6
Bence 'endl' kullanımı yürütmeyi yavaşlatabilir.
Krishna Mohan

2
Std :: endl kullanımı döngüde değildir.
nibot

Eşitlemenin açık veya kapalı olması fark etmez. Bunun için libc ++ 'yı suçlayın. Yalnızca libstdc ++
iBug'u

<cstdio> ve <stdio.h> arasında herhangi bir fark olacağını düşünüyor musunuz?
Chandrahas Aroori

iostreambir scanfçağrıda birden fazla tamsayıyı ayrıştırdığınızda kaybeder .
Maxim Egorushkin

68

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

Performans cin/ ' coutonlar altta yatan C kütüphanesi ile senkronize kendilerini tutmak gerekir çünkü yavaş olabilir. Hem C IO hem de C ++ IO kullanılacaksa bu çok önemlidir.

Ancak, yalnızca C ++ IO kullanacaksanız, herhangi bir GÇ işleminden önce aşağıdaki satırı kullanmanız yeterlidir.

std::ios::sync_with_stdio(false);

Bununla ilgili daha fazla bilgi için ilgili libstdc ++ belgelerine bakın .


Sadece yukarıdaki satırı kontrol ettim (std :: ios :: sync_with_stdio (false);) Ve gerçekten neredeyse cstdio kadar hızlı bir iostream yapıyor
gabrielhidasy

ayrıca cin.tie (static_cast <ostream *> (0)) kullanın; daha iyi performans için
Mohamed El-Nakib

42

Muhtemelen scanf, akışları kullanmaktan biraz daha hızlıdır. Akışlar çok sayıda tür güvenliği sağlasa ve çalışma zamanında biçim dizelerini ayrıştırmak zorunda olmasa da, genellikle aşırı bellek ayırmaları gerektirmeme avantajına sahiptir (bu, derleyicinize ve çalışma zamanınıza bağlıdır). Bununla birlikte, performans tek nihai hedefiniz değilse ve kritik yolda değilseniz, gerçekten daha güvenli (daha yavaş) yöntemleri tercih etmelisiniz.

Herb Sutter "tarafından burada yazılı çok lezzetli makale vardır Manor Farm Dize biçemleyicileri bir dize gibi biçimleyicisine performansının ayrıntı çok gider" sscanfve lexical_castve ne tür şeylerin yapıyor onları yavaş veya hızlı çalıştırın. Bu, muhtemelen C stili IO ve C ++ stili arasındaki performansı etkileyebilecek türden şeylere benzer. Biçimlendiriciler arasındaki temel fark, tür güvenliği ve bellek ayırma sayısı olma eğilimindeydi.


19

UVa Online'da bir problem üzerinde çalışarak bir akşam geçirdim (Factovisors, çok ilginç bir problem, kontrol edin):

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

Gönderimlerimde TLE (zaman sınırı aşıldı) alıyordum. Bu sorun çözme çevrimiçi jüri sitelerinde, çözümünüzü değerlendirmek için kullanılan potansiyel olarak binlerce test vakasını ele almak için 2-3 saniyelik bir zaman sınırınız vardır. Bunun gibi hesaplama açısından yoğun problemler için her mikrosaniye önemlidir.

Önerilen algoritmayı kullanıyordum (sitenin tartışma forumlarında okuyun), ancak hala TLE'leri alıyordum.

Sadece "cin >> n >> m" yi "scanf ("% d% d ", & n, & m)" ve birkaç küçük "couts" u "printfs" olarak değiştirdim ve TLE'm "Kabul Edildi" oldu!

Bu nedenle, evet, özellikle zaman sınırları kısa olduğunda büyük bir fark yaratabilir.


Katılıyorum. Aynısı UVA Çevrimiçi Hakim probleminde de başıma geldi: Army Buddies uva.onlinejudge.org/…
Mohamed El-Nakib

6

Hem performans hem de dize biçimlendirmesini önemsiyorsanız , Matthew Wilson'ın Hızlı Biçimlendirmesine bir göz atın kitaplığına .

düzenle - bu kitaplıktaki accu yayınına bağlantı: http://accu.org/index.php/journals/1539


Tamamen katılıyorum. Ancak, Hızlı Biçimlendirmenin yalnızca çıktı için olduğunu bilmeniz gerekir. Giriş / okuma olanakları yoktur. (Henüz değil, neyse)
dcw

Maalesef bu bağlantı kesilmiş görünüyor. İşte bir Wayback Machine kopyası: web.archive.org/web/20081222164527/http://fastformat.org
nibot

2

FILE * 'i bir C ++ streambuf olarak ve fprintf'i bir çalışma zamanı biçimi ayrıştırıcısı olarak uygulayan standart uygulamalar ( libio ) vardır. IOstreams çalışma zamanı formatının ayrıştırılmasına ihtiyaç duymaz, hepsi derleme zamanında yapılır. Bu nedenle, paylaşılan arka uçlarla, iostreams'in çalışma zamanında daha hızlı olmasını beklemek mantıklıdır.


Ben öyle düşünmüyorum. GNU'nun libc'sinin saf C ve assembly olduğunu düşünüyorum.
Chris Lutz

2

Evet, iostream cstdio'dan daha yavaştır.
Evet, C ++ ile geliştiriyorsanız muhtemelen cstdio kullanmamalısınız.
Biçimlendirme, yazım güvenliği, blah, blah, blah umurunuzda değilse, I / O almanın scanf'den daha hızlı yolları olduğunu söylemiştim ...

Örneğin bu, STDIN'den bir numara almak için özel bir rutindir:

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 () standart değildir ve görsel stüdyo değil gcc için kullanılabilir
Mohamed El-Nakib

2

İfadeleri cinve coutgenel kullanımda daha yavaş olduğu görülüyor scanfve printfC ++, ama aslında onlar HIZLI vardır!

Şeydir: In C ++, kullanmak ne zaman cinve coutbir senkronizasyon işlemi emin olur varsayılan olarak gerçekleştiği hem kullanıyorsanız scanfve cinbirbirleri ile senkronize olan programda, daha sonra ikisi de işi. Bu senkronizasyon işlemi zaman alır. Dolayısıyla cinvecout daha yavaş olması.

Ancak, senkronizasyon işlemi gerçekleşmeyecek şekilde ayarlanırsa cin, daha hızlıdır scanf.

Eşitleme sürecini atlamak için, aşağıdaki kod parçacığını programınızın hemen başına ekleyin main():

std::ios::sync_with_stdio(false);

Daha fazla bilgi için bu siteyi ziyaret edin .


Senkronizasyonla ilgili açıklamanız için +1. Senkronizasyonu yeni kapattım ve bazı kodlarda hem scanf hem de cin kullandım . şimdi neyin yanlış olduğunu biliyorum. teşekkür ederim!
Dariush

1

Sorun şu ki cin, size scanf()çağrıların üzerinde bir soyutlama katmanı verdiği için çok fazla ek yük vardır . C ++ yazılımı yazıyorsanız scanf()over kullanmamalısınız cinçünkü bu istek ciniçindir. Performans istiyorsanız, muhtemelen yine de C ++ ile G / Ç yazmazsınız.


2
cindaha (zamanında) gerçekten daha "soyut" scanf? Sanmıyorum ... scanfbiçim dizesini çalışma zamanında yorumlamalı, oysa iostreambiçimi derleme zamanında biliyor.
nibot

1
@nibot: Tür , derleme sırasında bilinir, ancak biçimi bilinmemektedir . Örneğin, girdinin onaltılık olmasının beklenip beklenmediği, tamamen çalışma zamanında nasıl std::istreamyapılandırıldığına bağlıdır (G / Ç manipülatörleri aracılığıyla veya nesnenin kendisinde bayraklar ayarlayarak ). Öte yandan bir nesnenin böyle bir durumu yoktur, bu nedenle bu bağlamda bir çağrı çok daha kararlıdır. 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;
}

Dosyanın sonunda bir hata var, ancak bu C kodu, daha hızlı C ++ sürümünden önemli ölçüde daha hızlı.

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

Orijinal C ++ 30 saniye, C kodu 2 saniye sürdü.


-1

Elbette cstdio'yu iostream yerine kullanmak saçma. En azından yazılım geliştirirken (eğer zaten c ++ 'ı c üzerinde kullanıyorsanız, o zaman sonuna kadar gidin ve sadece dezavantajlarından muzdarip olmak yerine faydalarını kullanın).

Ancak çevrimiçi yargıçta yazılım geliştirmiyorsunuz, Microsoft yazılımının 60 saniyede 3 saniyede başardığı şeyleri yapabilmesi gereken bir program oluşturuyorsunuz !!!

Yani, bu durumda, altın kural şöyle devam eder (tabii ki java kullanarak daha da fazla sorun yaşamazsanız)

  • Problemi çözmek için c ++ kullanın ve tüm gücünü (ve ağırlığını / yavaşlığını) kullanın
  • Sınırlı zamanınız varsa, printfs ve scanf'ler için cins ve cout'ları değiştirin (sınıf dizgisini kullanarak hata yaparsanız, şu şekilde yazdırın: printf (% s, mystr.c_str ());
  • Hala sınırlı zamanınız varsa, bazı belirgin optimizasyonlar yapmaya çalışın (örneğin / while / dowhiles için çok fazla gömülü veya özyinelemeli işlevlerden kaçınmak gibi). Ayrıca çok büyük olan referans nesnelerini de geçtiğinizden emin olun ...
  • Hala sınırlı zamanınız varsa, o zaman std :: vektörleri ve c dizileri için kümeleri değiştirmeyi deneyin.
  • Hala sınırlı zamanınız varsa, bir sonraki soruna geçin ...

-2

Daha scanfhızlı olsaydı bile cinfark etmezdi. Çoğu zaman, sabit sürücüden veya klavyeden okuyacaksınız. Ham verileri uygulamanıza almak, işlemek scanfveya cinişlemek için gerekenden çok daha fazla zaman alır .


Peki ya borularla IPC? Orada dikkat çekici bir performans çarpışması olabileceğini düşünüyor musunuz?
dreamlax

IPC ile borularda bile, çekirdeğe girip çıkarken scanf / cin ile ayrıştırmaktan çok daha fazla zaman harcanır.
Jay Conrod

8
Bu alanda testler yaptım ve kesinlikle cout & cin performansı kötüydü. Kullanıcı girdisi için önemsiz olsa da, performansın önemli olduğu şeyler için kesinlikle böyle değildir. Yine de daha hızlı olan başka c ++ çerçevesi var.
Johannes Schaub - litb

Sorun şu ki iostream , hdd'den daha yavaş. Evet, o kadar berbat.
polkovnikov.ph
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.