क्यों पायथन की तुलना में C ++ में स्टडिन बहुत धीमी है?


1838

मैं पाइथन और सी ++ का उपयोग करके स्टड से स्ट्रिंग इनपुट की रीडिंग लाइनों की तुलना करना चाहता था और मेरे सी ++ कोड को बराबर पायथन कोड की तुलना में परिमाण धीमी करने के क्रम को देखकर हैरान था। चूँकि मेरा C ++ रस्टी है और मैं अभी तक एक विशेषज्ञ पाइथोनिस्टा नहीं हूं, कृपया मुझे बताएं कि क्या मैं कुछ गलत कर रहा हूं या यदि मैं कुछ गलत कर रहा हूं।


(TLDR उत्तर: कथन को शामिल करें: cin.sync_with_stdio(false)या fgetsइसके बजाय केवल उपयोग करें ।

TLDR परिणाम: मेरे सवाल के नीचे तक सभी तरह से स्क्रॉल करें और तालिका देखें।)


C ++ कोड:

#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

पायथन समतुल्य:

#!/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))

यहाँ मेरे परिणाम हैं:

$ 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

मुझे ध्यान देना चाहिए कि मैंने मैक ओएस एक्स v10.6.8 (स्नो लेपर्ड) और लिनक्स 2.6.32 (रेड हैट लिनक्स 6.2) के तहत यह कोशिश की। पूर्व एक मैकबुक प्रो है, और बाद वाला एक बहुत ही मांसल सर्वर है, ऐसा नहीं है कि यह बहुत प्रासंगिक है।

$ 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

छोटे बेंचमार्क परिशिष्ट और पुनर्कथन

पूर्णता के लिए, मुझे लगा कि मैं मूल (समानार्थक) C ++ कोड के साथ एक ही बॉक्स पर एक ही फ़ाइल के लिए रीड स्पीड को अपडेट करूंगा। फिर, यह एक फास्ट डिस्क पर 100M लाइन फ़ाइल के लिए है। यहाँ कई समाधानों / दृष्टिकोणों के साथ तुलना की गई है:

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
क्या आपने कई बार अपने परीक्षण चलाए? शायद डिस्क कैश समस्या है।
वॉन काटो

9
@ जेजेसी: मुझे दो संभावनाएं दिखाई देती हैं (यह मानते हुए कि आपने डेविड द्वारा सुझाई गई कैशिंग समस्या को दूर किया है): 1) <iostream>प्रदर्शन बेकार है। पहली बार ऐसा नहीं हुआ। 2) अजगर लूप में डेटा को कॉपी न करने के लिए पर्याप्त चतुर है क्योंकि आप इसका उपयोग नहीं करते हैं। आप का उपयोग करने की कोशिश कर retest scanfऔर एक कर सकते हैं char[]। वैकल्पिक रूप से आप लूप को फिर से लिखने की कोशिश कर सकते हैं ताकि स्ट्रिंग के साथ कुछ किया जा सके (जैसे कि 5 वें अक्षर को रखें और परिणाम में इसे संक्षिप्त करें)।
जेएन

15
समस्या stdio के साथ सिंक्रनाइज़ेशन है - मेरा उत्तर देखें।
वॉन काटो

19
चूंकि किसी ने उल्लेख नहीं किया है कि आपको C ++ के साथ अतिरिक्त रेखा क्यों मिलती है: विरुद्ध परीक्षण न करें cin.eof()!! रखो getline'if` बयान में कॉल।
XIX

21
wc -lतेज है क्योंकि यह एक बार में एक से अधिक लाइन को स्ट्रीम करता है (यह fread(stdin)/memchr('\n')संयोजन हो सकता है)। अजगर के परिणाम परिमाण के समान क्रम में हैं, जैसेwc-l.py
jfs

जवाबों:


1644

डिफ़ॉल्ट रूप से, cinstdio के साथ सिंक्रनाइज़ किया जाता है, जो किसी भी इनपुट बफरिंग से बचने का कारण बनता है। यदि आप इसे अपने मुख्य के शीर्ष पर जोड़ते हैं, तो आपको बेहतर प्रदर्शन देखना चाहिए:

std::ios_base::sync_with_stdio(false);

आम तौर पर, जब एक इनपुट स्ट्रीम को बफ़र किया जाता है, तो एक बार में एक वर्ण को पढ़ने के बजाय, धारा को बड़े आकार में पढ़ा जाएगा। यह सिस्टम कॉल की संख्या को कम कर देता है, जो आमतौर पर अपेक्षाकृत महंगी होती हैं। हालांकि, चूंकि FILE*आधारित stdioऔर iostreamsअक्सर अलग-अलग कार्यान्वयन होते हैं और इसलिए अलग-अलग बफ़र्स होते हैं, इससे समस्या हो सकती है यदि दोनों एक साथ उपयोग किए गए थे। उदाहरण के लिए:

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

यदि cinवास्तव में आवश्यकता से अधिक इनपुट पढ़ा गया था , तो scanfफ़ंक्शन के लिए दूसरा पूर्णांक मान उपलब्ध नहीं होगा , जिसका अपना स्वतंत्र बफर है। इससे अप्रत्याशित परिणाम प्राप्त होंगे।

इससे बचने के लिए, डिफ़ॉल्ट रूप से, धाराओं के साथ सिंक्रनाइज़ किया जाता है stdio। इसे प्राप्त करने का एक सामान्य तरीका यह है कि cinप्रत्येक वर्ण को एक समय में stdioफ़ंक्शन के उपयोग के रूप में पढ़ा जाए । दुर्भाग्य से, यह बहुत अधिक उपरि का परिचय देता है। छोटी मात्रा में इनपुट के लिए, यह एक बड़ी समस्या नहीं है, लेकिन जब आप लाखों पंक्तियों को पढ़ रहे हैं, तो प्रदर्शन जुर्माना महत्वपूर्ण है।

सौभाग्य से, पुस्तकालय डिजाइनरों ने फैसला किया कि आपको इस प्रदर्शन को बेहतर बनाने के लिए इस सुविधा को अक्षम करने में सक्षम होना चाहिए अगर आपको पता था कि आप क्या कर रहे हैं, तो उन्होंने sync_with_stdioविधि प्रदान की ।


142
यह सबसे ऊपर होना चाहिए। यह लगभग निश्चित रूप से सही है। जवाब fscanfकॉल के साथ रीड को बदलने में झूठ नहीं बोल सकता है , क्योंकि यह बहुत ही सरलता से उतना काम नहीं करता है जितना कि पायथन करता है। पायथन को स्ट्रिंग के लिए मेमोरी आवंटित करनी चाहिए, संभवतः कई बार मौजूदा आवंटन को अपर्याप्त माना जाता है - ठीक उसी तरह जैसे कि C ++ दृष्टिकोण के साथ std::string। यह कार्य लगभग निश्चित रूप से I / O बाध्य है और std::stringC ++ में ऑब्जेक्ट बनाने या <iostream>स्वयं का उपयोग करने की लागत के बारे में बहुत अधिक FUD है।
कार्ल किंचल

51
हां, इस लाइन को मेरे मूल से तुरंत ऊपर जोड़ने के दौरान लूप ने कोड को अजगर से भी आगे कर दिया। मैं अंतिम संपादन के रूप में परिणाम पोस्ट करने वाला हूं। एक बार फिर धन्यवाद!
JJC

6
हाँ, यह वास्तव में cout, cerr और clog पर भी लागू होता है।
वॉन काटो

2
Cout, Cin, cerr और clog को तेज़ बनाने के लिए, इसे इस तरह से करें std :: ios_base :: सिंक_with_stdio (झूठा);
01100110

56
ध्यान दें कि sync_with_stdio()एक स्थिर सदस्य फ़ंक्शन है, और सभी मानक iostream ऑब्जेक्ट के cinलिए सिंक्रनाइज़ेशन पर या बंद किसी भी स्ट्रीम ऑब्जेक्ट (जैसे ) पर इस फ़ंक्शन को कॉल करता है ।
जॉन Zwinck

170

जिज्ञासा से बाहर मैंने हुड के नीचे क्या होता है पर एक नज़र डाली है, और मैंने प्रत्येक परीक्षण पर ड्रॉटस / स्ट्रेस का उपयोग किया है ।

सी ++

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

syscalls 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

अजगर

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

syscalls sudo dtruss -c ./a.py < in

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

159

मैं यहाँ कुछ साल पीछे हूँ, लेकिन:

मूल पोस्ट के 'संपादित 4/5/6' में, आप निर्माण का उपयोग कर रहे हैं:

$ /usr/bin/time cat big_file | program_to_benchmark

यह कुछ अलग तरीकों से गलत है:

  1. आप वास्तव में निष्पादन का समय तय कर रहे हैं cat, न कि आपके बेंचमार्क का। प्रदर्शित 'उपयोगकर्ता' और 'sys' सीपीयू उपयोग timeउन में से हैं cat, न कि आपके बेंचमार्क प्रोग्राम के। इससे भी बदतर, 'वास्तविक' समय भी आवश्यक रूप से सटीक नहीं है। catआपके स्थानीय ओएस में पाइपलाइनों के कार्यान्वयन और उसके आधार पर , यह संभव है कि catएक अंतिम विशालकाय बफर लिखता है और पाठक प्रक्रिया समाप्त होने से बहुत पहले बाहर निकलता है।

  2. catअनावश्यक का उपयोग और वास्तव में उल्टा है; आप चलते भागों को जोड़ रहे हैं। यदि आप एक पर्याप्त पुराने सिस्टम पर थे (यानी एक ही सीपीयू के साथ और - कंप्यूटर की कुछ पीढ़ियों में - मैं / ओ सीपीयू की तुलना में तेजी से) - तो जो एकमात्र तथ्य catचल रहा था वह काफी हद तक परिणामों को रंग सकता है। आप जो भी इनपुट और आउटपुट बफरिंग और अन्य प्रसंस्करण catकर सकते हैं, उसके अधीन हैं। ( अगर मैं रैंडल श्वार्ट्ज होता तो यह आपको 'बेकार यूज़ ऑफ कैट' पुरस्कार दिलवाता।)

एक बेहतर निर्माण होगा:

$ /usr/bin/time program_to_benchmark < big_file

इस कथन में यह वह शेल है जो big_file को खोलता है, इसे आपके प्रोग्राम (अच्छी तरह से, timeजो वास्तव में तब आपके प्रोग्राम को एक सबप्रोसेस के रूप में निष्पादित करता है) को पहले से ही ओपन फाइल डिस्क्रिप्टर के रूप में पास करता है। फ़ाइल रीडिंग का 100% सख्ती से उस प्रोग्राम की जिम्मेदारी है जिसे आप बेंचमार्क करने की कोशिश कर रहे हैं। यह आपको गंभीर जटिलताओं के बिना इसके प्रदर्शन का एक वास्तविक पढ़ने के लिए मिलता है।

मैं दो संभावितों का उल्लेख करूंगा, लेकिन वास्तव में गलत, 'फिक्स', जिन पर भी विचार किया जा सकता है (लेकिन मैं उन्हें अलग तरह से बताता हूं क्योंकि ये ऐसी चीजें नहीं हैं जो मूल पोस्ट में गलत थीं)

A. आप केवल अपने प्रोग्राम को टाइम करके इसे 'ठीक' कर सकते हैं:

$ cat big_file | /usr/bin/time program_to_benchmark

बी या पूरी पाइपलाइन समय से:

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

ये # 2 जैसे ही कारणों से गलत हैं: वे अभी भी catअनावश्यक रूप से उपयोग कर रहे हैं । मैं कुछ कारणों से उनका उल्लेख करता हूं:

  • वे उन लोगों के लिए अधिक 'प्राकृतिक' हैं जो POSIX शेल के I / O पुनर्निर्देशन सुविधाओं से पूरी तरह से सहज नहीं हैं

  • ऐसी स्थितियां हो सकती cat है जरूरत (जैसे: फ़ाइल पढ़ा जा करने के लिए उपयोग करने के लिए सौभाग्य की बात किसी प्रकार की आवश्यकता है, और आप बेंचमार्क जा करने के लिए कार्यक्रम के लिए है कि विशेषाधिकार प्रदान करने के लिए नहीं करना चाहते हैं: sudo cat /dev/sda | /usr/bin/time my_compression_test --no-output)

  • व्यवहार में , आधुनिक मशीनों पर, catपाइपलाइन में जोड़ा गया है शायद कोई वास्तविक परिणाम नहीं है।

लेकिन मैं कहता हूं कि आखिरी बात कुछ हिचकिचाहट के साथ। यदि हम 'एडिट 5' में अंतिम परिणाम की जाँच करते हैं -

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

- यह दावा है कि catपरीक्षण के दौरान सीपीयू का 74% खपत किया गया; और वास्तव में 1.34 / 1.83 लगभग 74% है। शायद एक रन:

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

केवल शेष .49 सेकंड लिया होगा! शायद नहीं: catयहां read()सिस्टम कॉल (या समतुल्य) के लिए भुगतान करना पड़ता था जो फ़ाइल को 'डिस्क' (वास्तव में बफर कैश) से स्थानांतरित करता था, साथ ही पाइप उन्हें वितरित करने के लिए लिखता है wc। सही परीक्षण अभी भी उन read()कॉलों को करना होगा ; केवल लिखने-से-पाइप और पढ़ने-से-पाइप कॉल सहेजे जाते थे, और वे बहुत सस्ते होने चाहिए।

फिर भी, मुझे लगता है आप के बीच के अंतर को मापने के लिए सक्षम हो जाएगा cat file | wc -lऔर wc -l < fileऔर एक ध्यान देने योग्य (2 अंकों प्रतिशत) अंतर पाते हैं। धीमे परीक्षणों में से प्रत्येक ने पूर्ण समय में एक समान दंड का भुगतान किया होगा; जो कि इसके बड़े कुल समय के एक छोटे से अंश के बराबर होगा।

वास्तव में, मैंने 1.5 गीगाबाइट फ़ाइल के साथ कुछ त्वरित परीक्षण किए, एक लिनक्स 3.13 (उबंटू 14.04) सिस्टम पर, इन परिणामों को प्राप्त करना (ये वास्तव में '3 सर्वश्रेष्ठ' परिणाम हैं; कैश को भड़काने के बाद):

$ 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)

ध्यान दें कि दो पाइपलाइन परिणाम वास्तविक दीवार-घड़ी समय की तुलना में अधिक CPU समय (उपयोगकर्ता + sys) लेने का दावा करते हैं। इसका कारण यह है कि मैं शेल (बैश) में निर्मित 'टाइम' कमांड का उपयोग कर रहा हूं, जो पाइप लाइन का संज्ञान है; और मैं एक मल्टी-कोर मशीन पर हूं, जहां एक पाइपलाइन में अलग-अलग प्रक्रियाएं अलग-अलग कोर का उपयोग कर सकती हैं, वास्तविक समय की तुलना में तेजी से सीपीयू समय जमा कर रहा है। /usr/bin/timeमैं वास्तविक समय की तुलना में छोटे CPU समय का उपयोग कर रहा हूं - यह दिखाते हुए कि यह केवल एक ही समय में पाइप लाइन तत्व को अपनी कमांड लाइन पर पारित कर सकता है। इसके अलावा, शेल का आउटपुट मिलीसेकंड देता है जबकि /usr/bin/timeकेवल सेकंड का सौवां हिस्सा देता है।

तो दक्षता के स्तर पर wc -l, catएक बड़ा अंतर बनाता है: 409/283 = 1.453 या 45.3% अधिक रीयलटाइम, और 775/280 = 2.768, या उपयोग किए गए 177% अधिक सीपीयू! मेरे यादृच्छिक पर यह समय-समय पर परीक्षण बॉक्स था।

मुझे यह जोड़ना चाहिए कि परीक्षण की इन शैलियों के बीच कम से कम एक महत्वपूर्ण अंतर है, और मैं यह नहीं कह सकता कि क्या यह एक लाभ या दोष है; आपको यह खुद तय करना होगा:

जब आप दौड़ते हैं cat big_file | /usr/bin/time my_program, तो आपका प्रोग्राम एक पाइप से इनपुट प्राप्त कर रहा है, ठीक उसी गति से भेजा गया है cat, और विखंडू लिखित रूप से बड़ा नहीं है cat

जब आप दौड़ते हैं /usr/bin/time my_program < big_file, तो आपका प्रोग्राम वास्तविक फ़ाइल के लिए एक ओपन फाइल डिस्क्रिप्टर प्राप्त करता है। आपका कार्यक्रम - या कई मामलों में भाषा के I / O पुस्तकालय जिसमें यह लिखा गया था - एक नियमित फ़ाइल को संदर्भित करने वाले फ़ाइल डिस्क्रिप्टर के साथ प्रस्तुत करने पर अलग-अलग कार्रवाई हो सकती है। यह mmap(2)स्पष्ट read(2)प्रणाली कॉल का उपयोग करने के बजाय, इनपुट फ़ाइल को उसके पता स्थान में मैप करने के लिए उपयोग कर सकता है । ये अंतर आपके बेंचमार्क परिणामों पर catबाइनरी चलाने की छोटी लागत की तुलना में कहीं अधिक बड़ा प्रभाव डाल सकते हैं ।

बेशक यह एक दिलचस्प बेंचमार्क परिणाम है अगर एक ही कार्यक्रम दोनों मामलों के बीच अलग-अलग प्रदर्शन करता है। यह दर्शाता है कि, वास्तव में, प्रोग्राम या इसके I / O लाइब्रेरी कुछ दिलचस्प काम कर रहे हैं, जैसे कि उपयोग करना mmap()। तो व्यवहार में दोनों तरीकों से बेंचमार्क चलाना अच्छा हो सकता है; शायद खुद catको चलाने की लागत को "माफ" करने के लिए कुछ छोटे कारक द्वारा परिणाम को छूट देना cat


26
वाह, यह काफी व्यावहारिक था! जबकि मुझे पता है कि कार्यक्रमों के स्टड के लिए इनपुट खिलाने के लिए बिल्ली अनावश्यक है और यह कि <शेल पुनर्निर्देशित को प्राथमिकता दी जाती है, मैं आमतौर पर डेटा के बाएं से दाएं प्रवाह के कारण बिल्ली से चिपक जाता हूं जो कि पूर्व विधि नेत्रहीन संरक्षित करता है जब मैं पाइपलाइनों के बारे में कारण। ऐसे मामलों में प्रदर्शन अंतर मैंने नगण्य पाया है। लेकिन, मैं आपकी शिक्षा की सराहना करता हूं, बेला।
JJC

11
पुनर्निर्देशन को एक प्रारंभिक चरण में शेल कमांड लाइन से बाहर रखा गया है, जो आपको इनमें से एक करने की अनुमति देता है, अगर यह बाएं से दाएं प्रवाह का अधिक मनभावन स्वरूप देता है: $ < big_file time my_program $ time < big_file my_program यह किसी भी पॉसिंक शेल में काम करना चाहिए (अर्थात `` शश नहीं `और मुझे` आरसी`:) की तरह एक्सोटिका के बारे में यकीन नहीं है
बेला लबकिन

6
एक ही समय में चल रहे `बिल्ली` द्विआधारी के कारण शायद अबाधित वृद्धिशील प्रदर्शन अंतर से अलग, आप परीक्षण की संभावना को मीमैप () इनपुट फ़ाइल में सक्षम होने के तहत छोड़ रहे हैं। इससे परिणामों में गहरा अंतर आ सकता है। यह सच है, भले ही आपने खुद को विभिन्न भाषाओं में बेंचमार्क लिखा हो, केवल एक फ़ाइल 'मुहावरे से उनकी इनपुट लाइनें' का उपयोग करते हुए। यह उनके विभिन्न I / O पुस्तकालयों के विस्तृत कामकाज पर निर्भर करता है।
बेला ल्यूबिन

2
साइड नोट: बैश का बिलिन timeपहले प्रोग्राम के बजाय पूरी पाइपलाइन को माप रहा है। time seq 2 | while read; do sleep 1; doneप्रिंट 2 सेकंड, /usr/bin/time seq 2 | while read; do sleep 1; doneप्रिंट 0 सेकंड।
लोकगीत

1
@ फोलकॉल - हां, << ध्यान दें कि दो पाइपलाइन परिणाम [शो] अधिक सीपीयू [की तुलना में] रीयलटाइम [का उपयोग कर] (बैश) अंतर्निहित 'समय' कमांड; ... / usr / bin / समय ... केवल एक ही समय में पाइप लाइन तत्व इसे अपनी कमांड लाइन पर पारित कर सकता है। >> '
बेला लबकिन

90

मैंने अपने कंप्यूटर पर एक मैक पर जी ++ का उपयोग करके मूल परिणाम को पुन: प्रस्तुत किया।

whileलूप में पायथन संस्करण के साथ इनलाइन लाने से ठीक पहले सी ++ संस्करण में निम्नलिखित कथनों को जोड़ना :

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

Sync_with_stdio ने 2 सेकंड में गति में सुधार किया, और एक बड़ा बफर सेट करके इसे 1 सेकंड तक नीचे लाया।


5
आप अधिक उपयोगी जानकारी प्राप्त करने के लिए विभिन्न बफर आकारों की कोशिश करना चाह सकते हैं। मुझे संदेह है कि आप तेजी से घटते हुए रिटर्न देखेंगे।
कार्ल केनचेल

8
मैं अपने जवाब में बहुत जल्दबाजी में था; बफ़र आकार को डिफ़ॉल्ट के अलावा किसी अन्य चीज़ पर सेट करने से प्रशंसनीय अंतर उत्पन्न नहीं हुआ।
3

109
मैं स्टैक पर 1 एमबी बफर स्थापित करने से भी बचूंगा। यह स्टैकओवरफ्लो को जन्म दे सकता है (हालांकि मुझे लगता है कि यह इसके बारे में बहस करने के लिए एक अच्छी जगह है!)
मैथ्यू एम।

11
मैथ्यू, मैक डिफ़ॉल्ट रूप से 8MB प्रक्रिया स्टैक का उपयोग करता है। लिनक्स 4MB प्रति थ्रेड डिफॉल्ट, IIRC का उपयोग करता है। 1MB अपेक्षाकृत उथले स्टैक गहराई के साथ इनपुट को बदलने वाले प्रोग्राम के लिए कोई समस्या नहीं है। इससे भी महत्वपूर्ण बात, हालांकि, std :: यदि बफर गुंजाइश से बाहर जाता है, तो स्टैक कचरा फेंक देगा।
SEK

22
@SEK विंडोज डिफ़ॉल्ट स्टैक का आकार 1 एमबी है।
.tienne

39

getline, scanfयदि आप फ़ाइल लोडिंग समय के बारे में परवाह नहीं करते हैं या यदि आप छोटे पाठ फ़ाइलों को लोड कर रहे हैं तो स्ट्रीम ऑपरेटर, सुविधाजनक हो सकते हैं। लेकिन, अगर प्रदर्शन कुछ ऐसी चीज है जिसके बारे में आप परवाह करते हैं, तो आपको वास्तव में पूरी फाइल को मेमोरी में बफर करना चाहिए (यह मानते हुए कि यह फिट होगा)।

यहाँ एक उदाहरण है:

//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();

यदि आप चाहें, तो आप इस तरह से अधिक सुविधाजनक पहुंच के लिए उस बफर के चारों ओर एक धारा लपेट सकते हैं:

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

इसके अलावा, यदि आप फ़ाइल के नियंत्रण में हैं, तो पाठ के बजाय एक फ्लैट बाइनरी डेटा प्रारूप का उपयोग करने पर विचार करें। यह पढ़ना और लिखना अधिक विश्वसनीय है क्योंकि आपको व्हाट्सएप की सभी अस्पष्टताओं से निपटना नहीं है। यह पार्स करने के लिए बहुत छोटा और बहुत तेज़ है।


20

निम्न कोड मेरे लिए अब तक यहां पोस्ट किए गए अन्य कोड की तुलना में तेज था: (विजुअल स्टूडियो 2013, 64-बिट, 500 एमबी फाइल लाइन की लंबाई के साथ समान रूप से [0, 1000) में)।

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'; });
}

यह एक कारक 2 से अधिक मेरे सभी पायथन प्रयासों को हरा देता है।


आप एक छोटे से कस्टम लेकिन पूरी तरह से सीधे सी प्रोग्राम के साथ उस से भी तेज गति प्राप्त कर सकते हैं जो पुनरावृत्त रूप readसे लंबाई के एक स्थिर बफर में BUFSIZEया समान समरूप mmapsyscalls के माध्यम से असंबद्ध बनाता है, और फिर उस बफ़र के माध्यम से व्हिप करता है for (char *cp = buf; *cp; cp++) count += *cp == "\n"। आपको BUFSIZEअपने सिस्टम के लिए ट्यून करना होगा , हालाँकि, जो stdio आपके लिए पहले ही कर चुका होगा। लेकिन उस forलूप को आपके बॉक्स के हार्डवेयर के लिए तेजी से असेंबल करने वाले भाषा निर्देश को संक्षिप्त रूप से संकलित करना चाहिए।
tchrist

3
count_if और एक लैम्ब्डा भी "awesomely screaming- फास्ट असेंबलर" को संकलित करता है।
पेट्टर

17

वैसे, C ++ संस्करण के लिए लाइन की गिनती पायथन संस्करण के लिए गणना की तुलना में एक से अधिक है कारण यह है कि ईओएफ ध्वज केवल तब सेट होता है जब ईओएफ से परे पढ़ने का प्रयास किया जाता है। तो सही लूप होगा:

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

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

70
वास्तव में सही लूप होगा: while (getline(cin, input_line)) line_count++;
जोनाथन वेकली

2
@JonathanWakely मुझे पता है कि मैं बहुत देर से हूँ, लेकिन उपयोग करें ++line_count;और नहीं line_count++;
वैल कहते हैं, मोनिका

7
@ अगर कोई फर्क पड़ता है कि आपके कंपाइलर में बग है। चर एक है long, और संकलक यह बताने में काफी सक्षम है कि वेतन वृद्धि का उपयोग नहीं किया गया है। यदि यह पुनर्जन्म और पुनर्जन्म के लिए समान कोड उत्पन्न नहीं करता है, तो यह टूट गया है।
जोनाथन वेकेली

2
वास्तव में कोई भी सभ्य संकलक पोस्ट-इन्क्रीमेंट के दुरुपयोग का पता लगाने में सक्षम होगा और इसके बजाय पूर्व-वृद्धि द्वारा इसे प्रतिस्थापित कर सकता है, लेकिन संकलक के लिए आवश्यक नहीं हैं । तो नहीं, यह भी नहीं टूटा है अगर कंपाइलर प्रतिस्थापन का प्रदर्शन नहीं करता है। इसके अलावा, लिखने के ++line_count;बजाय line_count++;चोट नहीं लगेगी :)
Fareanor

1
@valsaysReinstateMonica इस विशिष्ट उदाहरण में, किसी एक को क्यों पसंद किया जाएगा? परिणाम यहां किसी भी तरह से उपयोग नहीं किया जाता है, इसलिए इसे whileसही के बाद पढ़ा जाएगा ? अगर किसी प्रकार की त्रुटि थी और क्या आप यह सुनिश्चित करना चाहते हैं कि यह line_countसही है? मैं सिर्फ अनुमान लगा रहा हूं लेकिन मुझे समझ नहीं आ रहा है कि यह क्यों मायने रखेगा।
टैंकरस्मैश

14

अपने दूसरे उदाहरण में (स्कैनफ () के साथ) कारण यह अभी भी धीमा है क्योंकि स्कैनफ ("% s") पार्स स्ट्रिंग और किसी भी अंतरिक्ष चार (अंतरिक्ष, टैब, न्यूलाइन) की तलाश करता है।

इसके अलावा, हाँ, सीपीथॉन हार्डडिस्क रीड से बचने के लिए कुछ कैशिंग करता है।


12

उत्तर का पहला तत्व: <iostream>धीमा है। बहुत धीमी गति से। मुझे scanfनीचे की तरह एक विशाल प्रदर्शन को बढ़ावा मिलता है , लेकिन यह पायथन की तुलना में अभी भी दो गुना धीमा है।

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

जब तक मैंने अपना तीसरा संपादन नहीं किया, तब तक यह पोस्ट नहीं देखी, लेकिन आपके सुझाव के लिए फिर से धन्यवाद। अजीब बात है, वहाँ मेरे लिए कोई 2x हिट नहीं है अजगर ऊपर अब edit3 में स्कैन लाइन के साथ। मैं 2.7 का उपयोग कर रहा हूँ, वैसे।
JJC

10
C ++ संस्करण को ठीक करने के बाद, यह stdio संस्करण मेरे कंप्यूटर पर c ++ iostreams संस्करण की तुलना में काफी धीमा है। (3 सेकंड बनाम 1 सेकंड)
करुन्स्की

10

ठीक है, मुझे लगता है कि अपने दूसरे समाधान में आप से बदल cinकरने के लिए scanfहै, जो (CIN sloooooooooooow है) पहला सुझाव मैं तुम्हें करने के लिए जा रहा था था। अब, अगर आप से स्विच scanfकरने के लिए fgets, आप प्रदर्शन में एक और बढ़ावा देखेंगे: fgetsस्ट्रिंग इनपुट के लिए सबसे तेजी से सी ++ कार्य है।

BTW, उस सिंक चीज़ के बारे में नहीं जानता था, अच्छा। लेकिन आपको फिर भी कोशिश करनी चाहिए fgets


2
सिवाय इसके fgetsगलत होगा (लाइन काउंट के संदर्भ में, और छोरों में बंटवारे की रेखाओं के संदर्भ में यदि आपको वास्तव में उपयोग करने की आवश्यकता है) पर्याप्त रूप से बड़ी लाइनों के लिए, अधूरी लाइनों के लिए अतिरिक्त जांच के बिना (और इसकी भरपाई करने के प्रयास में अनावश्यक रूप से बड़े बफ़र्स आवंटित करना शामिल है। , जहां std::getlineवास्तविक इनपुट को मूल रूप से मिलान करने के लिए वास्तविककरण को संभालता है)। तेज और गलत आसान है, लेकिन यह लगभग हमेशा "थोड़ा धीमा, लेकिन सही" का उपयोग करने के लिए लायक है, जो sync_with_stdioआपको बंद कर देता है।
शैडो रेंजर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.