C ++ 'da stdin'den satır okuma neden Python'dan daha yavaş?


1839

Python ve C ++ kullanarak stdin dize giriş okuma satırlarını karşılaştırmak istedim ve benim C ++ kod eşdeğer Python kodundan daha yavaş bir büyüklük sırasını çalıştırmak görmek için şok oldu. C ++'m paslı olduğu ve henüz uzman bir Pythonista olmadığım için lütfen bana yanlış bir şey mi yapıp yapmadığımı veya bir şeyi yanlış anladığımı söyle.


(TLDR yanıtı: ifadeyi ekleyin: cin.sync_with_stdio(false)veya sadece kullanın fgets.

TLDR sonuçları: sorumun en altına kadar kaydırın ve tabloya bakın.)


C ++ kodu:

#include <iostream>
#include <time.h>

using namespace std;

int main() {
    string input_line;
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    while (cin) {
        getline(cin, input_line);
        if (!cin.eof())
            line_count++;
    };

    sec = (int) time(NULL) - start;
    cerr << "Read " << line_count << " lines in " << sec << " seconds.";
    if (sec > 0) {
        lps = line_count / sec;
        cerr << " LPS: " << lps << endl;
    } else
        cerr << endl;
    return 0;
}

// Compiled with:
// g++ -O3 -o readline_test_cpp foo.cpp

Python Eşdeğeri:

#!/usr/bin/env python
import time
import sys

count = 0
start = time.time()

for line in  sys.stdin:
    count += 1

delta_sec = int(time.time() - start_time)
if delta_sec >= 0:
    lines_per_sec = int(round(count/delta_sec))
    print("Read {0} lines in {1} seconds. LPS: {2}".format(count, delta_sec,
       lines_per_sec))

İşte sonuçlarım:

$ cat test_lines | ./readline_test_cpp
Read 5570000 lines in 9 seconds. LPS: 618889

$cat test_lines | ./readline_test.py
Read 5570000 lines in 1 seconds. LPS: 5570000

Bunu hem Mac OS X v10.6.8 (Snow Leopard) hem de Linux 2.6.32 (Red Hat Linux 6.2) altında denediğimi belirtmeliyim. Birincisi bir MacBook Pro'dur ve ikincisi çok etli bir sunucudur, bu çok ilgili değildir.

$ for i in {1..5}; do echo "Test run $i at `date`"; echo -n "CPP:"; cat test_lines | ./readline_test_cpp ; echo -n "Python:"; cat test_lines | ./readline_test.py ; done
Test run 1 at Mon Feb 20 21:29:28 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 2 at Mon Feb 20 21:29:39 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 3 at Mon Feb 20 21:29:50 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 4 at Mon Feb 20 21:30:01 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 5 at Mon Feb 20 21:30:11 EST 2012
CPP:   Read 5570001 lines in 10 seconds. LPS: 557000
Python:Read 5570000 lines in  1 seconds. LPS: 5570000

Küçük karşılaştırma eki ve özet

Tamlık için, orijinal (senkronize) C ++ koduyla aynı kutuda aynı dosyanın okuma hızını güncelleyeceğimi düşündüm. Yine, bu hızlı bir diskte 100M çizgi dosyası içindir. İşte birkaç çözüm / yaklaşımla karşılaştırma:

Implementation      Lines per second
python (default)           3,571,428
cin (default/naive)          819,672
cin (no sync)             12,500,000
fgets                     14,285,714
wc (not fair comparison)  54,644,808

14
Testlerinizi birçok kez yaptınız mı? Belki de bir disk önbellek sorunu var.
Vaughn Cato

9
@JJC: İki olasılık görüyorum (David'in önerdiği önbellek sorununu ortadan kaldırdığınız varsayılarak): 1) <iostream>performans kötü. İlk kez değil. 2) Python, for döngüsünde verileri kopyalamayacak kadar zekidir, çünkü kullanmazsınız. Kullanmaya çalışırken tekrar test edebilirsiniz scanfve a char[]. Alternatif olarak, dize ile bir şeyler yapmak için döngüyü yeniden yazmayı deneyebilirsiniz (örn. 5. harfi koruyun ve sonuç olarak birleştirin).
JN

15
Sorun stdio ile senkronizasyon - cevabımı görün.
Vaughn Cato

19
Kimse neden C ++ ile ekstra bir çizgi olsun bahsetmiş gibi görünmüyor: karşı test etmeyin cin.eof()!! getlineÇağrıyı 'if` ifadesine koyun .
Xeo

21
wc -lhızlıdır, çünkü akışı aynı anda birden fazla satır okur ( fread(stdin)/memchr('\n')kombinasyon olabilir ). Python sonuçları aynı büyüklük sıralamasındadır, örn.,wc-l.py
jfs

Yanıtlar:


1644

Varsayılan olarak, cinherhangi bir girdi arabelleğe alınmasını önlemek için stdio ile senkronize edilir. Bunu ana makinenizin üstüne eklerseniz, çok daha iyi performans görmelisiniz:

std::ios_base::sync_with_stdio(false);

Normalde, bir giriş akışı arabelleğe alındığında, her seferinde bir karakter okumak yerine, akış daha büyük parçalar halinde okunur. Bu, tipik olarak nispeten pahalı olan sistem çağrılarının sayısını azaltır. Bununla birlikte, FILE*temelli stdiove iostreamsgenellikle ayrı uygulamalar ve dolayısıyla ayrı arabelleklere sahip olduğundan, her ikisi birlikte birlikte kullanılırsa bir soruna yol açabilir. Örneğin:

int myvalue1;
cin >> myvalue1;
int myvalue2;
scanf("%d",&myvalue2);

cinGerçekte gerekenden daha fazla girdi okunmuşsa scanf, kendi bağımsız arabelleği olan işlev için ikinci tamsayı değeri kullanılamaz . Bu beklenmedik sonuçlara yol açacaktır.

Bunu önlemek için varsayılan olarak akışlar senkronize edilir stdio. Bunu başarmanın yaygın yollarından cinbiri, stdioişlevleri kullanarak her karakteri birer birer okumaktır . Ne yazık ki, bu çok fazla yük getiriyor. Az miktarda girdi için, bu büyük bir sorun değildir, ancak milyonlarca satırı okurken performans cezası önemlidir.

Neyse ki, kütüphane tasarımcıları, ne yaptığınızı biliyorsanız daha iyi performans elde etmek için bu özelliği devre dışı bırakabilmeniz gerektiğine karar verdiler, böylece sync_with_stdioyöntemi sağladılar .


142
Bu en üstte olmalıdır. Neredeyse kesinlikle doğrudur. Cevap, okumayı bir fscanfçağrı ile değiştirmede yalan söyleyemez , çünkü bu Python'un yaptığı kadar çok işe yaramaz. Python, varolan ayırmanın yetersiz olduğu için, muhtemelen C ++ yaklaşımı gibi, dizeye bellek ayırmalıdır std::string. Bu görev neredeyse kesinlikle G / Ç ile bağlantılıdır ve std::stringC ++ 'da nesne oluşturma veya <iostream>kendi başına kullanmanın maliyeti hakkında çok fazla FUD vardır .
Karl Knechtel

51
Evet, bu satır orijinalimin hemen üstüne eklenirken döngü python'u bile aşmak için kodu hızlandırdı. Sonuçları son düzenleme olarak göndermek üzereyim. Tekrar teşekkürler!
JJC

6
Evet, bu aslında cout, cerr ve clog için de geçerlidir.
Vaughn Cato

2
Cout, cin, cerr ve clog'u daha hızlı hale getirmek için bu şekilde yapın std :: ios_base :: sync_with_stdio (false);
01100110

56
Bunun sync_with_stdio()statik bir üye işlevi olduğunu ve herhangi bir akış nesnesindeki (örneğin cin) bu işleve yapılan bir çağrının tüm standart iostream nesneleri için senkronizasyonu açıp kapattığını unutmayın .
John Zwinck

171

Meraktan sadece başlık altında neler olduğuna bir göz attım ve her testte dtruss / strace kullandım .

C ++

./a.out < in
Saw 6512403 lines in 8 seconds.  Crunch speed: 814050

sistem çağrıları sudo dtruss -c ./a.out < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            6
pread                                           8
mprotect                                       17
mmap                                           22
stat64                                         30
read_nocancel                               25958

piton

./a.py < in
Read 6512402 lines in 1 seconds. LPS: 6512402

sistem çağrıları sudo dtruss -c ./a.py < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            5
pread                                           8
mprotect                                       17
mmap                                           21
stat64                                         29

159

Burada birkaç yıl kaldım ama:

Orijinal gönderinin '4/5/6' düzeninde, yapıyı kullanıyorsunuz:

$ /usr/bin/time cat big_file | program_to_benchmark

Bu birkaç farklı şekilde yanlıştır:

  1. Aslında cat, karşılaştırmalı değerlendirmenizin değil , yürütmenin zamanlamasını yapıyorsunuz . 'Kullanıcı' ve 'sys' CPU kullanımı tarafından görüntülenen timeaittir catdeğil benchmarked programda. Daha da kötüsü, 'gerçek' zaman da kesin olarak doğru değildir. catYerel işletim sisteminizde ve boru hatlarının uygulanmasına bağlı olarak, catson bir dev tamponu yazması ve okuyucu işlemi işini bitirmeden çok önce çıkması mümkündür .

  2. Kullanımı catgereksizdir ve aslında ters etki yaratır; hareketli parçalar ekliyorsunuz. Yeterince eski bir sistemdeyseniz (örneğin, tek bir CPU ile ve - belirli bilgisayar nesillerinde - CPU'dan daha hızlı G / Ç) - catçalışan gerçek , sonuçları önemli ölçüde renklendirebilir. Ayrıca, girdi ve çıktı tamponlama ve diğer işlemlerin catyapabileceği her şeye tabi olursunuz . ( Eğer Randal Schwartz olsaydım bu sana muhtemelen 'Yararsız Kedi Kullanımı' ödülü kazandıracaktı.

Daha iyi bir yapı:

$ /usr/bin/time program_to_benchmark < big_file

Bu açıklamada öyle kabuk (aslında hiç iyi programınıza geçirerek big_file açar timehalihazırda açık dosya tanımlayıcı olarak daha sonra alt süreç olarak programı çalıştırır olan). Dosya okumasının% 100'ü kesinlikle karşılaştırmaya çalıştığınız programın sorumluluğundadır. Bu size sahte komplikasyonlar olmadan performansının gerçek bir okumasını sağlar.

Ben de düşünülebilir iki olası, ama aslında yanlış, 'düzeltmeler' söz edeceğiz (ama orijinal yazı yanlış olan şey değildir çünkü onları farklı 'numara'):

C. Bunu yalnızca programınızı zamanlayarak 'düzeltebilirsiniz':

$ cat big_file | /usr/bin/time program_to_benchmark

B. veya tüm boru hattını zamanlayarak:

$ /usr/bin/time sh -c 'cat big_file | program_to_benchmark'

Bunlar # 2 ile aynı nedenlerden dolayı yanlış: hala catgereksiz yere kullanıyorlar . Onlardan birkaç nedenden dolayı bahsediyorum:

  • POSIX kabuğunun I / O yönlendirme olanaklarıyla tamamen rahat olmayan insanlar için daha 'doğal'

  • Orada durumlar olabilir cat edilir gerekli (örn: okunacak dosya erişimi için ayrıcalık çeşit gerektirir ve Kıyaslama yapılacak olan programa bu ayrıcalığı vermek istemiyorum: sudo cat /dev/sda | /usr/bin/time my_compression_test --no-output)

  • pratikte , modern makinelerde, catboru hattına eklenenlerin muhtemelen gerçek bir sonucu yoktur.

Ama bu son şeyi tereddüt ederek söylüyorum. Son sonucu '5'i Düzenle' olarak incelersek -

$ /usr/bin/time cat temp_big_file | wc -l
0.01user 1.34system 0:01.83elapsed 74%CPU ...

- bu cat, test sırasında CPU'nun% 74'ünü tükettiğini iddia ediyor ; ve aslında 1.34 / 1.83 yaklaşık% 74'tür. Belki de:

$ /usr/bin/time wc -l < temp_big_file

sadece kalan .49 saniyeyi alacaktı! Muhtemelen hayır: catburada read()dosyayı 'disk'ten (aslında arabellek önbelleği) aktaran sistem çağrıları (veya eşdeğeri) ve ayrıca boru onları teslim etmek için yazar wc. Doğru test hala bu read()çağrıları yapmak zorunda kalacaktı ; yalnızca boruya yazma ve borudan okuma aramaları kaydedilmiş olacak ve bunlar oldukça ucuz olmalıdır.

Yine de, sizin arasındaki farkı ölçmek mümkün olacaktır tahmin cat file | wc -lve wc -l < fileve farkedilir (2 basamaklı yüzdesi) farkı bulmak. Daha yavaş testlerin her biri, mutlak sürede benzer bir ceza ödemiş olacaktır; ancak bu toplam süresinin daha küçük bir kısmını oluşturur.

Aslında, bir Linux 3.13 (Ubuntu 14.04) sisteminde 1.5 gigabaytlık çöp dosyasıyla bazı hızlı testler yaptım, bu sonuçları elde ettim (bunlar aslında '3'ün en iyisi' sonuçlarıdır; elbette önbelleği hazırladıktan sonra):

$ time wc -l < /tmp/junk
real 0.280s user 0.156s sys 0.124s (total cpu 0.280s)
$ time cat /tmp/junk | wc -l
real 0.407s user 0.157s sys 0.618s (total cpu 0.775s)
$ time sh -c 'cat /tmp/junk | wc -l'
real 0.411s user 0.118s sys 0.660s (total cpu 0.778s)

İki boru hattı sonucunun gerçek duvar saati süresinden daha fazla CPU zamanı (kullanıcı + sys) aldığını iddia edin. Bunun nedeni, boru hattının farkında olan shell (bash) 'in yerleşik' time 'komutunu kullanmamdır; ve bir boru hattındaki ayrı işlemlerin ayrı çekirdekler kullanabileceği ve CPU süresini gerçek zamanlıdan daha hızlı biriktirdiği çok çekirdekli bir makinedeyim. Kullanarak /usr/bin/timegerçek zamanlıdan daha küçük CPU zamanı görüyorum - komut satırında yalnızca tek boru hattı öğesinin kendisine geçebileceğini gösteriyor. Ayrıca, kabuğun çıktısı milisaniye verirken /usr/bin/timesadece saniyenin yüzde biri verir.

Yani verimliliği düzeyinde wc -l, cat409/283 = 1.453 veya daha fazla gerçek zamanlı 45.3%, ve = 2,768 775/280 veya bir kuyruklu 177% daha fazla CPU kullandı: büyük bir fark yaratıyor! O zamanlar orada olduğum rastgele test kutusunda.

Bu test stilleri arasında en az bir başka önemli fark olduğunu eklemeliyim ve bunun bir fayda ya da hata olup olmadığını söyleyemem; buna kendiniz karar vermelisiniz:

Çalıştırdığınızda cat big_file | /usr/bin/time my_program, programınız bir borudan, tam olarak gönderilen hızda catve yazılandan daha büyük olmayan parçalar halinde girdi alır cat.

Çalıştırdığınızda /usr/bin/time my_program < big_file, programınız gerçek dosyaya açık bir dosya tanımlayıcısı alır. Programınız - veya çoğu durumda yazıldığı dilin G / Ç kütüphaneleri - normal bir dosyaya başvuran bir dosya tanımlayıcıyla sunulduğunda farklı işlemler yapabilir. Bu kullanabilir mmap(2)yerine açık kullanmak yerine, kendi adres alanı içine girdi dosyasını eşleştirmek için read(2)sistem çağrıları. Bu farkların, kıyaslama sonuçlarınız üzerinde, catikili çalıştırmanın küçük maliyetinden çok daha büyük bir etkisi olabilir .

Tabii ki aynı program iki vaka arasında önemli ölçüde farklı performans gösteriyorsa, bu ilginç bir kıyaslama sonucudur. Bu gösteriyor ki, gerçekten, program veya I / O kütüphaneleri vardır kullanmak gibi, ilginç bir şey yapıyor mmap(). Bu yüzden pratikte ölçütleri her iki şekilde çalıştırmak iyi olabilir; belki de catsonucu, catkendini çalıştırma maliyetini "affetmek" için küçük bir faktörle iskonto etmek .


26
Vay be, bu oldukça anlayışlıydı! Kedinin programların standartlarına girdi beslemesi için gerekli olmadığının ve <kabuk yönlendirmesinin tercih edildiğinin farkında olduğum halde, eski yöntemin görsel olarak koruduğu soldan sağa veri akışı nedeniyle genellikle kediye yapıştım boru hatları hakkında aklıma geldiğimde. Bu gibi durumlarda performans farkları ihmal edilebilir buldum. Ama, bizi eğittiğin için minnettarım Bela.
JJC

11
Yönlendirme, kabuk komut satırından erken bir aşamada ayrıştırılır, bu da soldan sağa akışın daha hoş bir görünümünü verirse bunlardan birini yapmanızı sağlar: $ < big_file time my_program $ time < big_file my_program Bu herhangi bir POSIX kabuğunda (yani csh değil) çalışmalıdır. `` rc` gibi exotica hakkında emin değilim :)
Bela Lubkin

6
Yine, aynı anda `` cat '' ikili çalıştırılmasından dolayı belki de ilginç olmayan artan performans farkının yanı sıra, test edilen programın giriş dosyasını mmap () yapabilme olasılığından vazgeçiyorsunuz. Bu sonuçlarda derin bir fark yaratabilir. Kriterleri çeşitli dillerde, sadece 'dosyadan giriş satırlarını' deyimini kullanarak yazmış olsanız bile bu doğrudur. Çeşitli I / O kütüphanelerinin ayrıntılı çalışmalarına bağlıdır.
Bela Lubkin

2
Yan not: Bash yerleşkesi time, ilk program yerine tüm boru hattını ölçüyor. time seq 2 | while read; do sleep 1; done2 saniye /usr/bin/time seq 2 | while read; do sleep 1; donebasar, 0 saniye basar.
folkol

1
@folkol - evet, << İki boru hattının gerçek zamanlı [kullanarak] (Bash) yerleşik 'zaman' komutunu [[]] daha fazla CPU gösterdiğine dikkat edin; ... / usr / bin / time ... komut satırında kendisine iletilen tek bir boru hattı elemanını yalnızca zamanlayabilir. >> '
Bela Lubkin

90

Mac'te g ++ kullanarak orijinal sonucu bilgisayarımda çoğalttım.

Aşağıdaki ifadeleri whiledöngüden hemen önce C ++ sürümüne eklemek, Python sürümü ile aynı satıra getirir :

std::ios_base::sync_with_stdio(false);
char buffer[1048576];
std::cin.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

sync_with_stdio hızı 2 saniyeye yükseltti ve daha büyük bir arabellek ayarlamak 1 saniyeye indirdi.


5
Daha yararlı bilgiler edinmek için farklı arabellek boyutlarını denemek isteyebilirsiniz. Hızla azalan getiri göreceğinizden şüpheleniyorum.
Karl Knechtel

8
Cevabımda çok aceleci davrandım; arabellek boyutunu varsayılandan farklı bir değere ayarlamak kayda değer bir fark yaratmadı.
karunski

109
Ayrıca yığın üzerinde 1 MB arabellek ayarlamaktan kaçınır. Stackoverflow yol açabilir (sanırım bu konuda tartışmak için iyi bir yer!)
Matthieu M.

11
Matthieu, Mac varsayılan olarak 8MB'lık bir işlem yığını kullanır. Linux, iş parçacığı varsayılanı IIRC başına 4 MB kullanır. 1MB, girdiyi nispeten sığ yığın derinliğine dönüştüren bir program için o kadar da önemli değildir. Daha da önemlisi, std :: cin arabellek kapsam dışına çıkarsa yığını çöpe atacaktır.
SEK

22
@SEK Windows varsayılan Yığın boyutu 1 MB'dir.
Étienne

39

getline, akış operatörleri,, scanfdosya yükleme süresini önemsemiyorsanız veya küçük metin dosyaları yüklüyorsanız uygun olabilir. Ancak, performans önem verdiğiniz bir şeyse, tüm dosyayı gerçekten belleğe almalısınız (uygun olacağını varsayarak).

İşte bir örnek:

//open file in binary mode
std::fstream file( filename, std::ios::in|::std::ios::binary );
if( !file ) return NULL;

//read the size...
file.seekg(0, std::ios::end);
size_t length = (size_t)file.tellg();
file.seekg(0, std::ios::beg);

//read into memory buffer, then close it.
char *filebuf = new char[length+1];
file.read(filebuf, length);
filebuf[length] = '\0'; //make it null-terminated
file.close();

İsterseniz, aşağıdaki gibi daha rahat erişim için bir akışı bu arabellek etrafına sarabilirsiniz:

std::istrstream header(&filebuf[0], length);

Ayrıca, dosyayı kontrol ediyorsanız, metin yerine düz bir ikili veri formatı kullanmayı düşünün. Okumak ve yazmak daha güvenilir çünkü boşlukların tüm belirsizlikleri ile uğraşmak zorunda değilsiniz. Ayrıştırmak da daha küçük ve çok daha hızlı.


20

Aşağıdaki kod benim için şimdiye kadar burada yayınlanan diğer koddan daha hızlı oldu: (Visual Studio 2013, 64 bit, satır uzunluğu eşit [0, 1000 olarak 500 MB dosya)).

const int buffer_size = 500 * 1024;  // Too large/small buffer is not good.
std::vector<char> buffer(buffer_size);
int size;
while ((size = fread(buffer.data(), sizeof(char), buffer_size, stdin)) > 0) {
    line_count += count_if(buffer.begin(), buffer.begin() + size, [](char ch) { return ch == '\n'; });
}

Tüm Python girişimlerimi bir faktör 2'den daha fazla yeniyor.


Tamponlanmamış readsistem çağrılarını statik bir tampon belleğe BUFSIZEveya eşdeğer ilgili mmapsistem çağrıları aracılığıyla yinelemeli olarak yapan küçük bir özel ancak tamamen basit C programı ile bundan daha da hızlı olabilir ve daha sonra bu tamponu à la ile sarar for (char *cp = buf; *cp; cp++) count += *cp == "\n". BUFSIZEYine de, hangi stdio'nun sizin için daha önce yapacağı sisteminizi ayarlamanız gerekir. Ancak bu fordöngü, kutunuzun donanımı için çığlık atan hızlı bir montajcı dili talimatları için derlenmelidir.
tchrist

3
count_if ve lambda da "korkunç çığlık atan hızlı toplayıcı" yı derler.
Petter

17

Bu arada, C ++ sürümü için satır sayısının Python sürümü için sayıdan daha fazla olmasının nedeni, eof bayrağının yalnızca eof'in ötesinde okuma girişiminde bulunulmasıdır. Doğru döngü şöyle olur:

while (cin) {
    getline(cin, input_line);

    if (!cin.eof())
        line_count++;
};

70
Gerçekten doğru döngü şöyle olurdu: while (getline(cin, input_line)) line_count++;
Jonathan Wakely

2
@JonathanWakely Oldukça geç kaldığımı biliyorum, ama kullan ++line_count;ve kullan line_count++;.
val diyor Reinstate Monica

7
@val, derleyicinizde bir hata varsa herhangi bir fark yaratırsa. Değişken a 'dır longve derleyici artışın sonucunun kullanılmadığını söyleyebilir. Postincrement ve preincrement için özdeş kod üretmezse, bozuktur.
Jonathan Wakely

2
Gerçekten de iyi bir derleyici artım sonrası kötüye kullanımı algılayabilecek ve bunun yerine bir ön artışla değiştirebilecek, ancak derleyicilere gerek yoktur . Yani hayır, derleyici değiştirmeyi yapmasa bile bozulmaz. Dahası, yazmak ++line_count;yerine yazmak line_count++;zarar
vermez

1
@valsaysReinstateMonica Bu özel örnekte, neden biri tercih edilir? Sonuç burada her iki şekilde de kullanılmaz, bu yüzden sonra okunur while, değil mi? Bir tür hata olup olmadığı ve line_countdoğru olduğundan emin olmak ister misiniz ? Sadece tahmin ediyorum ama bunun neden önemli olduğunu anlamıyorum.
TankorSmash

14

İkinci örneğinizde (scanf ()) bunun hala daha yavaş olmasının nedeni, scanf ("% s") dizeyi ayrıştırması ve herhangi bir boşluk karakteri (boşluk, sekme, yeni satır) araması olabilir.

Ayrıca, evet, CPython sabit disk okumalarını önlemek için bazı önbellekleme yapar.


12

Bir cevabın ilk unsuru: <iostream>yavaş. Kahretsin yavaş. scanfAşağıda olduğu gibi büyük bir performans artışı elde ediyorum , ancak yine de Python'dan iki kat daha yavaş.

#include <iostream>
#include <time.h>
#include <cstdio>

using namespace std;

int main() {
    char buffer[10000];
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    int read = 1;
    while(read > 0) {
        read = scanf("%s", buffer);
        line_count++;
    };
    sec = (int) time(NULL) - start;
    line_count--;
    cerr << "Saw " << line_count << " lines in " << sec << " seconds." ;
    if (sec > 0) {
        lps = line_count / sec;
        cerr << "  Crunch speed: " << lps << endl;
    } 
    else
        cerr << endl;
    return 0;
}

Üçüncü düzenlememi yapana kadar bu yayını görmedim, ancak öneriniz için tekrar teşekkürler. Garip bir şekilde, yukarıdaki edit3'te scanf satırı ile şimdi python'a karşı 2x isabet yok. Bu arada 2.7 kullanıyorum.
JJC

10
C ++ sürümü düzeltildikten sonra, bu stdio sürümü bilgisayarımdaki c ++ iostreams sürümünden oldukça yavaştır. (3 saniye vs 1 saniye)
karunski

10

Eh, ikinci çözümde size gelen anahtarlamalı görüyoruz ciniçin scanf(cin sloooooooooooow olan) Sana yapacaktım ilk öneri olduğu,. Eğer geçiş Şimdi, eğer scanfüzere fgets, performanstaki başka bir destek görecekti: fgetsdize girişi için en hızlı C ++ işlevidir.

BTW, bu senkronizasyon şeyi bilmiyordu, güzel. Ama yine de denemelisin fgets.


2
Dışında fgetseksik satırlar için ek kontroller olmadan (ve bunu telafi etmeye çalışmak gereksiz yere büyük arabelleklerin tahsis edilmesini içerir) , burada std::getlinegerçek girdiyi sorunsuz bir şekilde eşleştirmek için yeniden tahsisi işler). Hızlı ve yanlış kolaydır, ancak neredeyse her zaman "biraz daha yavaş, ama doğru" kullanmaya değer sync_with_stdio.
ShadowRanger
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.