පයිතන්ට වඩා C ++ හි stdin වෙතින් රේඛා කියවීම මන්දගාමී වන්නේ ඇයි?


1861

පයිතන් සහ සී ++ භාවිතා කරමින් ස්ටැඩින් වෙතින් කියවීමේ රේඛා කියවීම සැසඳීමට මට අවශ්‍ය වූ අතර මගේ සී ++ කේතය සමාන පයිතන් කේතයට වඩා විශාල අනුපිළිවෙලක් ධාවනය වන බව දැක කම්පනයට පත් විය. මගේ C ++ මලකඩ නිසා සහ මම තවමත් පයිතොනිස්ටා විශේෂ expert යෙකු නොවන බැවින්, කරුණාකර මට කියන්න මම යම්කිසි වැරැද්දක් කරන්නේ නම් හෝ මම යමක් වරදවා වටහාගෙන ඇත්නම්.


(TLDR පිළිතුර: ප්‍රකාශය ඇතුළත් කරන්න: cin.sync_with_stdio(false)නැතහොත් fgetsඒ වෙනුවට භාවිතා කරන්න.

ටීඑල්ඩීආර් ප්‍රති results ල: මගේ ප්‍රශ්නයේ පහළට පහළට අනුචලනය කර මේසය දෙස බලන්න.)


සී ++ කේතය:

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

මෙන්න මගේ ප්‍රති results ල:

$ 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

මැක් ඕඑස් එක්ස් වී 10.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

16
ඔබ ඔබේ පරීක්ෂණ කිහිප වතාවක් ක්‍රියාත්මක කළාද? සමහර විට තැටි හැඹිලි ගැටලුවක් තිබේ.
වෝන් කැටෝ

9
JJJC: මම හැකියාවන් දෙකක් දකිමි (ඩේවිඩ් විසින් යෝජනා කරන ලද හැඹිලි ගැටළුව ඔබ ඉවත් කර ඇතැයි උපකල්පනය කරන්න): 1) <iostream>කාර්ය සාධනය උරා ගනී. එය සිදු වූ පළමු අවස්ථාව නොවේ. 2) පයිතන් ඔබ ලූපය සඳහා දත්ත පිටපත් නොකිරීමට තරම් දක්ෂය. ඔබ භාවිතා කිරීමට උත්සාහ යළි හැකි scanfසහ char[]. විකල්පයක් ලෙස ඔබට ලූපය නැවත ලිවීමට උත්සාහ කළ හැකිය, එවිට යමක් නූල් සමඟ කළ හැකිය (උදා: 5 වන අකුර තබාගෙන ප්‍රති result ලයක් ලෙස එය සංයුක්ත කරන්න).
ජේඑන්

16
ගැටළුව වන්නේ stdio සමඟ සමමුහුර්ත කිරීමයි - මගේ පිළිතුර බලන්න.
වෝන් කැටෝ

19
ඔබ C ++ සමඟ අමතර රේඛාවක් ලබා ගන්නේ මන්දැයි කිසිවෙකු සඳහන් කර නැති නිසා: එරෙහිව පරීක්ෂා නොකරන්න cin.eof()!! මෙම දාන්න getlineඇති 'if` ප්රකාශයක් බවට ඇමතුමක්.
Xeo

21
wc -lඑය වේගවත් වන්නේ එය වරකට එක පේළියකට වඩා ධාරාව කියවන බැවිනි (එය fread(stdin)/memchr('\n')සංයෝජනය විය හැකිය ). පයිතන් wc-l.py
ප්‍රති results ල

Answers:


1667

tl; dr: C ++ හි විවිධ පෙරනිමි සැකසුම් නිසා තවත් පද්ධති ඇමතුම් අවශ්‍ය වේ.

පෙරනිමියෙන්, 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කාර්යයන් භාවිතා කර කියවීමයි . අවාසනාවකට මෙන්, මෙය පොදු කාර්ය ගොඩක් හඳුන්වා දෙයි. කුඩා ප්‍රමාණයේ ආදාන සඳහා, මෙය විශාල ගැටළුවක් නොවේ, නමුත් ඔබ පේළි මිලියන ගණනක් කියවන විට, කාර්ය සාධන ද penalty ුවම සැලකිය යුතු ය.

වාසනාවකට මෙන්, පුස්තකාල නිර්මාණකරුවන් තීරණය කළේ ඔබ කරන්නේ කුමක්දැයි ඔබ දන්නේ නම් වැඩි දියුණු කරන ලද කාර්ය සාධනයක් ලබා ගැනීම සඳහා ඔබට මෙම අංගය අක්‍රිය කළ හැකි බවයි sync_with_stdio.


144
මෙය ඉහළින්ම තිබිය යුතුය. එය නිසැකවම පාහේ නිවැරදි ය. කියවීම fscanfඇමතුමකින් ප්‍රතිස්ථාපනය කිරීමේදී පිළිතුරට බොරු කිව නොහැක , මන්ද එය පයිතන් කරන තරම් කාර්යයක් නොකරන බැවිනි. පයිතන් විසින් නූල සඳහා මතකය වෙන් කළ යුතුය, දැනට පවතින ප්‍රතිපාදන ප්‍රමාණවත් නොවන බව සලකන විට කිහිප වතාවක්ම - හරියටම C ++ ප්‍රවේශය සමඟ std::string. මෙම කර්තව්‍යය නිසැකවම පාහේ I / O බැඳී std::stringඇති අතර C ++ හි වස්තූන් නිර්මාණය කිරීමේ පිරිවැය හෝ <iostream>තමන් විසින්ම භාවිතා කිරීමේ පිරිවැය පිළිබඳව ඕනෑවට වඩා FUD ඇත.
කාල් නෙක්ටෙල්

52
ඔව්, මෙම පේළිය මගේ මුල් පිටපතට ඉහළින් එක් කිරීමෙන් ලූප් පයිතන් පවා ඉක්මවා යන ලෙස කේතය වේගවත් කළේය. මම අවසාන සංස්කරණය ලෙස ප්‍රති results ල පළ කිරීමට සූදානම් වෙමි. නැවත ස්තූතියි!
JJC

6
ඔව්, මෙය ඇත්ත වශයෙන්ම කෝට්, සර් සහ ක්ලෝග් සඳහාද අදාළ වේ.
වෝන් කැටෝ

2
Cout, cin, cerr සහ clog වේගවත් කිරීම සඳහා, මේ ආකාරයට කරන්න std :: ios_base :: sync_with_stdio (අසත්‍ය);
01100110

57
බව සටහන sync_with_stdio()ස්ථිතික සාමාජික ශ්රිතය වන අතර, ඕනෑම විෂය ධාරාව වස්තුව (උදා: මෙම ශ්රිතය වෙත දුරකථන ඇමතුමක් cinසඳහා සමමුහුර්ත හෝ අක්රිය කරන්න) ටොගල සියලු සම්මත iostream වස්තූන්.
ජෝන් ස්වින්ක්

174

කුතුහලයෙන් යුතුව මම කබාය යටතේ සිදුවන්නේ කුමක්දැයි සොයා බැලුවෙමි, තවද මම එක් එක් පරීක්ෂණය සඳහා dtruss / strace භාවිතා කර ඇත්තෙමි .

සී ++

./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

164

මම මෙතන අවුරුදු කීපයක් පසුපසින් සිටිමි, නමුත්:

මුල් පෝස්ට් එකේ 4/5/6 සංස්කරණය කරන්න, ඔබ ඉදිකිරීම් භාවිතා කරන්නේ:

$ /usr/bin/time cat big_file | program_to_benchmark

මෙය විවිධ ආකාර දෙකකින් වැරදියි:

  1. ඔබ ඇත්ත වශයෙන්ම කාලය මිස ක්‍රියාත්මක කිරීමට කාලය catමිස ඔබේ මිණුම් ලකුණ නොවේ. මෙම 'පරිශීලක' සහ 'sys' CPU භාවිතයක් විසින් ප්රදර්ශනය timeඅයගේ catඔබගේ පිළිගත් වැඩසටහන නැහැ,. ඊටත් වඩා භයානක දෙය නම්, 'සැබෑ' කාලය ද අනිවාර්යයෙන්ම නිවැරදි නොවේ. catඔබේ දේශීය මෙහෙයුම් පද්ධතියේ නල මාර්ග ක්‍රියාත්මක කිරීම හා ක්‍රියාත්මක කිරීම මත පදනම්ව , catඅවසාන යෝධ බෆරයක් ලිවීමට සහ පා er කයාගේ ක්‍රියාවලිය අවසන් වීමට බොහෝ කලකට පෙර පිටවීමට ඉඩ ඇත.

  2. භාවිතය catඅනවශ්‍ය වන අතර ඇත්ත වශයෙන්ම ප්‍රති p ලදායක ය; ඔබ චලනය වන කොටස් එකතු කරනවා. ඔබ ප්‍රමාණවත් තරම් පැරණි පද්ධතියක සිටියේ නම් (එනම් තනි CPU එකක් සහ - සමහර පරම්පරාවේ පරිගණක වල ​​- CPU ට වඩා වේගයෙන් I / O) - ක්‍රියාත්මක වන විට පමණක් ප්‍රති cat.ල සැලකිය යුතු ලෙස වර්ණ ගැන්විය හැකිය. ආදාන සහ ප්‍රතිදාන බෆරින් සහ වෙනත් සැකසුම් catමඟින් කළ හැකි ඕනෑම දෙයකට ඔබ යටත් වේ . ( මම රැන්ඩල් ෂ්වාට්ස් නම් මෙය ඔබට 'වැඩකට නැති කැට්' සම්මානය ලබා දෙනු ඇත.

වඩා හොඳ ඉදිකිරීමක් වනුයේ:

$ /usr/bin/time program_to_benchmark < big_file

මෙම ප්‍රකාශයේ දී එය බිග්_ෆයිල් විවෘත කරන කවචය වන අතර එය ඔබේ වැඩසටහනට යවයි (හොඳයි, ඇත්ත වශයෙන්ම timeඔබේ වැඩසටහන උපප්‍රොසෙස් ලෙස ක්‍රියාත්මක කරයි) දැනටමත් විවෘත ගොනු විස්තරයක් ලෙස. ලිපිගොනු කියවීමෙන් 100% ක්ම ඔබ මිණුම් ලකුණක් කිරීමට උත්සාහ කරන වැඩසටහනේ වගකීම වේ. ව්‍යාජ සංකූලතා නොමැතිව එහි ක්‍රියාකාරිත්වය පිළිබඳ සැබෑ කියවීමක් මෙය ඔබට ලබා දෙයි.

සලකා බැලිය හැකි, නමුත් නිවැරදිව කළ හැකි 'නිවැරදි කිරීම්' දෙකක් මම සඳහන් කරමි (නමුත් මේවා මුල් පෝස්ට් එකේ වැරදි වූ ඒවා නොවන බැවින් මම ඒවා වෙනස් ලෙස අංකනය කරමි):

පිළිතුර - ඔබට මෙය නිවැරදි කළ හැක්කේ ඔබේ වැඩසටහනේ වේලාව අනුව පමණි:

$ cat big_file | /usr/bin/time program_to_benchmark

B. හෝ සම්පූර්ණ නල මාර්ගය කාලානුරූපව:

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

# 2 හා සමාන හේතු නිසා මේවා වැරදියි: ඒවා තවමත් catඅනවශ්‍ය ලෙස භාවිතා කරයි. හේතු කිහිපයක් නිසා මම ඒවා සඳහන් කරමි:

  • පොසික්ස් කවචයේ I / O යළි හරවා යැවීමේ පහසුකම් සමඟ සම්පුර්ණයෙන්ම සැපපහසු නොවන පුද්ගලයින් සඳහා ඒවා වඩාත් ස්වාභාවිකය

  • අවස්ථා තිබිය හැකි cat වේ අවශ්ය (උදා: කියවිය යුතු ගොනුව ප්රවේශ වරප්රසාද යම් ආකාරයක අවශ්ය, සහ ඔබ කොමර්ෂල් බැංකුවේ කිරීමට වැඩසටහන සඳහා එම වරප්රසාදය ලබා දීමට අවශ්ය නැත: sudo cat /dev/sda | /usr/bin/time my_compression_test --no-output)

  • ප්‍රායෝගිකව , නවීන යන්ත්‍ර මත, catනල මාර්ගයට එකතු කිරීම සැබෑ ප්‍රතිවිපාකයක් නොවනු ඇත.

නමුත් මම ඒ අන්තිම දේ කියන්නේ යම් පැකිලීමකින්. 'සංස්කරණය 5' හි අවසාන ප්‍රති result ලය අප විමසා බැලුවහොත් -

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

- catපරීක්ෂණය අතරතුර CPU වලින් 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 ඉලක්කම් ප්රතිශතයක්) වෙනස සොයා ගන්න. සෑම මන්දගාමී පරීක්ෂණයකටම නිරපේක්ෂ වේලාවට සමාන ද penalty ුවමක් ගෙවනු ඇත; කෙසේ වෙතත් එය එහි විශාල මුළු කාලයෙන් කුඩා කොටසකට සමාන වේ.

ඇත්ත වශයෙන්ම මම ලිනක්ස් 3.13 (උබුන්ටු 14.04) පද්ධතියක් මත ගිගාබයිට් 1.5 ක කුණු ගොනුවක් සමඟ ඉක්මන් පරීක්ෂණ කිහිපයක් සිදු කළෙමි, මෙම ප්‍රති results ල ලබාගෙන ඇත (මේවා සැබවින්ම 'හොඳම 3 ප්‍රති results ල'; හැඹිලිය ප්‍රාථමික කිරීමෙන් පසුව, ඇත්ත වශයෙන්ම):

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

නල මාර්ග ප්‍රති results ල දෙක සැබෑ බිත්ති ඔරලෝසු කාලයට වඩා CPU කාලය (පරිශීලක + sys) ගත් බව සලකන්න. මෙයට හේතුව නල මාර්ගය පිළිබඳ සංජානනීය වන ෂෙල් (බාෂ්) හි 'ටයිම්' විධානය භාවිතා කිරීමයි; මම බහුකාර්ය යන්ත්‍රයක සිටින අතර නල මාර්ගයක වෙනම ක්‍රියාදාමයන්ට වෙනම හරයක් භාවිතා කළ හැකි අතර තථ්‍ය කාලයට වඩා වේගයෙන් CPU කාලය රැස් කර ගත හැකිය. භාවිතා කිරීම /usr/bin/timeතථ්‍ය කාලයට වඩා කුඩා CPU වේලාවක් මා දකිමි - එය පෙන්වන්නේ තනි විධාන රේඛාවක් එහි විධාන රේඛාවට සම්ප්‍රේෂණය කළ හැකි කාලය පමණි. එසේම, කවචයේ ප්‍රතිදානය මිලි තත්පර /usr/bin/timeලබා දෙන අතර තත්පරයෙන් සියයෙන් එකක් පමණක් ලබා දෙයි.

ඒ නිසා කාර්යක්ෂමතාව මට්ටමින් wc -l, මෙම catවිශාල වෙනසක්: වැඩි සජීව 409/283 = 1.453 හෝ 45.3% ක්, සහ 775/280 = 2.768, හෝ CPU භාවිතා තව 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ද්විමය ධාවනය සඳහා වන කුඩා පිරිවැයට වඩා ඔබේ මිණුම් සලකුණු ප්‍රති results ල කෙරෙහි වඩා විශාල බලපෑමක් ඇති කළ හැකිය .

එකම වැඩසටහන අවස්ථා දෙක අතර සැලකිය යුතු ලෙස වෙනස් ලෙස ක්‍රියා කරන්නේ නම් ඇත්ත වශයෙන්ම එය සිත්ගන්නා සුළු ප්‍රති result ලයකි. එය, නියත වශයෙන්ම, මෙම වැඩසටහන හෝ එහි I / O පුස්තකාල පෙන්නුම් කරන්නේ යම් දෙයක් රසවත්, භාවිතා කරන්නේ ඇද mmap(). එබැවින් ප්‍රායෝගිකව මිණුම් සලකුණු දෙයාකාරයෙන්ම ක්‍රියාත්මක කිරීම හොඳ විය හැකිය; catධාවනය කිරීමේ පිරිවැය "සමාව දීමට" සමහර කුඩා සාධක මගින් ප්‍රති result ලය වට්ටම් කිරීම cat.


28
ඇවැත්නි, එය තරමක් තීක්ෂ්ණ බුද්ධියකි! වැඩසටහන් වල ආදානය පෝෂණය කිරීම සඳහා බළලා අනවශ්‍ය බවත් <ෂෙල් යළි-යොමුවීම වඩාත් සුදුසු බවත් මම දැන සිටියද, මම සාමාන්‍යයෙන් බළලුන්ට ඇලී සිටින්නේ දත්ත වමේ සිට දකුණට ගලායාම නිසාය. මම නල මාර්ග ගැන තර්ක කරන විට. එවැනි අවස්ථාවන්හි කාර්ය සාධන වෙනස්කම් නොසැලකිලිමත් බව මට පෙනී ගියේය. නමුත්, ඔබ අපව දැනුවත් කිරීම අගය කරනවා, බෙලා.
JJC

11
නැවත හරවා යැවීම මුල් අවධියේදී ෂෙල් විධාන රේඛාවෙන් විග්‍රහ කර ඇති අතර, එය වමේ සිට දකුණට ප්‍රවාහයේ වඩාත් ප්‍රසන්න පෙනුමක් ලබා දෙන්නේ නම්, මේවායින් එකක් කිරීමට ඔබට ඉඩ සලසයි: $ < big_file time my_program $ time < big_file my_program මෙය ඕනෑම පොසික්ස් කවචයක ක්‍රියා කළ යුතුය (එනම් `සීඑස් නොවේ `සහ rc` වැනි එක්සොටිකා ගැන මට විශ්වාස නැත :)
බෙලා ලුබ්කින්

7
නැවතත්, 'බළලා' ද්විමය එකවර ක්‍රියාත්මක වීම නිසා ඇති විය හැකි සමහර විට උනන්දුවක් නොදක්වන කාර්ය සාධන වෙනස පසෙක තබා, ඔබ පරීක්ෂණයට ලක්ව ඇති වැඩසටහනට ආදාන ගොනුව () සිතියම් ගත කිරීමට ඇති හැකියාව අතහැර දමයි. මෙය ප්‍රති .ලවල ප්‍රබල වෙනසක් ඇති කළ හැකිය. විවිධ භාෂාවලින් ඔබ විසින්ම මිණුම් සලකුණු ලිවුවද මෙය සත්‍යයකි, ඔවුන්ගේ 'ගොනුවකින් ආදාන රේඛා' භාවිතා කිරීම. එය ඔවුන්ගේ විවිධ I / O පුස්තකාලවල සවිස්තරාත්මක ක්‍රියාකාරකම් මත රඳා පවතී.
බෙලා ලුබ්කින්

2
පැති සටහන: timeපළමු වැඩසටහන වෙනුවට බාෂ්ගේ බිල්ඩින් මුළු නල මාර්ගයම මනිනු ලබයි. time seq 2 | while read; do sleep 1; doneතත්පර 2 ක්, තත්පර /usr/bin/time seq 2 | while read; do sleep 1; done0 ක් මුද්‍රණය කරයි.
folkol

2
olfolkol - ඔව්, << නල මාර්ග ප්‍රති results ල දෙක තථ්‍ය කාලයට වඩා [CPU] පෙන්වන බව සැලකිල්ලට ගන්න [බාෂ්) ගේ 'කාල' විධානය භාවිතා කරමින්; . >> '
බෙලා ලුබ්කින්

91

මැක්හි g ++ භාවිතා කරමින් මම මගේ පරිගණකයේ මුල් ප්‍රති result ලය ප්‍රතිනිෂ්පාදනය කළෙමි.

ලූපයට පෙර පහත සඳහන් ප්‍රකාශයන් C ++ අනුවාදයට එක් 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
මගේ පිළිතුරට මම ඉක්මන් විය; බෆරයේ ප්‍රමාණය පෙරනිමිය හැර වෙනත් දෙයකට සැකසීම සැලකිය යුතු වෙනසක් ඇති කළේ නැත.
karunski

109
1MB බෆරයක් සිරස් අතට තැබීමෙන්ද මම වැළකී සිටිමි. එය ස්ටෝක් ඕවර් ප්‍රවාහයට මඟ පෑදිය හැකිය (මම හිතන්නේ ඒ ගැන විවාද කිරීමට මෙය හොඳ තැනක්!)
මැතිව් එම්.

11
මැතිව්, මැක් පෙරනිමියෙන් 8MB ක්‍රියාවලි තොගයක් භාවිතා කරයි. ලිනක්ස් නූල් පෙරනිමියකට 4MB භාවිතා කරයි, IIRC. 1MB යනු නොගැඹුරු තොග ගැඹුරකින් ආදානය පරිවර්තනය කරන වැඩසටහනකට එතරම් ගැටළුවක් නොවේ. වැදගත්ම දෙය නම්, බෆරය විෂය පථයෙන් බැහැර වුවහොත් std :: cin තොගය කුණු කූඩයට දමනු ඇත.
SEK

22
@SEK වින්ඩෝස් පෙරනිමි තොගයේ ප්‍රමාණය 1MB වේ.
අයිටියන්

41

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 MB ගොනුව රේඛීය දිග ඒකාකාරව [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 ට වඩා පරාජය කරයි.


1
ඉතා කුඩා සිරිතක් සහිත නමුත් සම්පූර්ණයෙන්ම සරල C වැඩසටහනක් මඟින් ඔබට ඊටත් වඩා වේගයෙන් ලබා ගත හැකි අතර එය නැවත නැවතත් නොකැඩූ සිස්කල් readස්ථිතික බෆරයක් බවට BUFSIZEහෝ ඊට සමාන අනුරූප mmapසිස්කල් හරහා සිදු කරයි , ඉන්පසු නව රේඛා ගණන් කරන බෆරය හරහා කස පහර දෙයි for (char *cp = buf; *cp; cp++) count += *cp == "\n". BUFSIZEඔබේ පද්ධතිය සඳහා සුසර කිරීමට ඔබට සිදුවනු ඇත , කෙසේ වෙතත්, ඔබ වෙනුවෙන් දැනටමත් කර ඇති stdio එක. නමුත් එම forලූපය ඔබේ කොටුවේ දෘඩාංග සඳහා කෑගැසීමේ-වේගවත් එකලස් කිරීමේ භාෂා උපදෙස් වලට සම්පාදනය කළ යුතුය.
tchrist

4
count_if සහ lambda ද "භයානක ලෙස කෑගැසීමේ-වේගවත් එකලස් කරන්නෙකු" වෙත සම්පාදනය කරයි.
පීටර්

18

මාර්ගය වන විට, සී ++ අනුවාදය සඳහා පේළි ගණන පයිතන් අනුවාදයේ ගණන් කිරීමට වඩා විශාල වීමට හේතුව නම්, ඊඕෆ් ධජය සකසනු ලබන්නේ ඊඕෆ් ඉක්මවා කියවීමට උත්සාහ කළ විට පමණි. එබැවින් නිවැරදි ලූපය වනුයේ:

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

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

71
ඇත්ත වශයෙන්ම නිවැරදි ලූපය වනුයේ: while (getline(cin, input_line)) line_count++;
ජොනතන් වේක්ලි

3
On ජොනතන් වේක්ලි මම දන්නවා මම සෑහෙන්න පරක්කුයි කියලා, නමුත් පාවිච්චි කරන්නේ ++line_count;නැහැ line_count++;.
val පවසන්නේ රෙනේස්ටේට් මොනිකා

8
@val එයින් යම් වෙනසක් සිදු වුවහොත් ඔබේ සම්පාදකයාට දෝෂයක් ඇත. විචල්‍යය a longවන අතර, වර්ධකයේ ප්‍රති result ලය භාවිතා නොකරන බව පැවසීමට සම්පාදකයාට තරමක් හැකියාවක් ඇත. Postincrement සහ preincrement සඳහා සමාන කේතයක් ජනනය නොකරන්නේ නම්, එය කැඩී ඇත.
ජොනතන් වේක්ලි

3
ඇත්ත වශයෙන්ම ඕනෑම යහපත් සම්පාදකයෙකුට පශ්චාත් වර්ධක අනිසි භාවිතය හඳුනා ගැනීමට හැකි වන අතර ඒ වෙනුවට පූර්ව වර්ධකයක් මගින් එය ආදේශ කළ හැකිය, නමුත් සම්පාදකයින් අවශ්‍ය නොවේ . එබැවින් නැත, සම්පාදකයා ආදේශනය සිදු නොකළද එය කැඩී නැත. එපමණක් නොව, ++line_count;ඒ වෙනුවට ලිවීමෙන් line_count++;කිසිදු හානියක් සිදු නොවනු ඇත :)
Fareanor

1
specificvalsaysReinstateMonica මෙම විශේෂිත උදාහරණයේ දී, එක් අයෙකු කැමති වන්නේ ඇයි? ප්‍රති result ලය මෙහි දෙයාකාරයෙන්ම භාවිතා නොවේ, එබැවින් එය කියවනු ලැබේ 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;
}

මම මගේ තුන්වන සංස්කරණය කරන තුරු මෙම ලිපිය දුටුවේ නැත, නමුත් ඔබගේ යෝජනාවට නැවතත් ස්තූතියි. පුදුමයට කරුණක් නම්, ඉහත සංස්කරණ 3 හි ස්කෑන්ෆ් රේඛාව සමඟ මට එදිරිව පයිතන්ට එදිරිව 2x පහරක් නොමැත. මම 2.7 භාවිතා කරමි.
JJC

10
C ++ අනුවාදය සවි කිරීමෙන් පසුව, මෙම stdio අනුවාදය මගේ පරිගණකයේ ඇති c ++ iostreams අනුවාදයට වඩා සැලකිය යුතු ලෙස මන්දගාමී වේ. (තත්පර 3 එදිරිව තත්පර 1)
කරුන්ස්කි

10

හොඳයි, මම ඔබේ දෙවන විසඳුමක් ඔබ මාරු බව දකින්න cinකිරීමට scanfමම ඔබට කරන්න (cin sloooooooooooow මිල) යන පළමු යෝජනාව වූ,. දැන්, ඔබ වෙත මාරු scanfවුවහොත් fgets, කාර්ය සාධනයේ තවත් තල්ලුවක් ඔබට පෙනෙනු ඇත: fgetsනූල් ආදානය සඳහා වේගවත්ම C ++ ශ්‍රිතය වේ.

බීටීඩබ්ලිව්, ඒ සමමුහුර්ත දේ ගැන දැනගෙන හිටියේ නැහැ, හොඳයි. නමුත් ඔබ තවමත් උත්සාහ කළ යුතුයි fgets.


3
හැර fgetsවැරදි (රේඛාව චෝදනා අනුව, සහ වළළු හරහා පැලෙන රේඛා අනුව ඇත්ත වශයෙන්ම ඔබ ඒවා භාවිතා කිරීමට අවශ්ය නම්) තරම් විශාල මාර්ග සඳහා, අසම්පූර්ණ රේඛා සඳහා අතිරේක පරික්ෂා කිරීම් වලින් තොරව වනු ඇත (සහ ඒ සඳහා වන්දි ගෙවීම සඳහා උත්සාහ අනවශ්ය විශාල අවරෝධක වෙන් කිරීමයි , std::getlineසත්‍ය ආදානය බාධාවකින් තොරව ගැලපීම සඳහා නැවත වෙන් කිරීම හසුරුවයි). වේගවත් හා වැරදි පහසුය, නමුත් "තරමක් මන්දගාමී, නමුත් නිවැරදි" භාවිතා කිරීම සැමවිටම පාහේ වටී, එය නිවා දැමීම sync_with_stdioඔබට ලැබේ.
ෂැඩෝ රේන්ජර්
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.