স্টিডিনের পাঠ্য রেখাটি পাইথনের তুলনায় সি ++-তে আরও ধীর গতিতে কেন?


1836

আমি পাইথন এবং সি ++ ব্যবহার করে স্টিডিনের স্ট্রিং ইনপুটগুলির পড়ার রেখাগুলির তুলনা করতে চেয়েছিলাম এবং আমার সি ++ কোডটি সমতুল্য পাইথন কোডের চেয়ে ধীরে ধীরে প্রস্থের ক্রমটি চালানো দেখে হতবাক হয়েছিল। যেহেতু আমার সি ++ টি জঞ্জাল এবং আমি এখনও পাইথনিস্টা বিশেষজ্ঞ নই, দয়া করে আমাকে বলুন যে আমি কিছু ভুল করছি বা আমি কিছু ভুল বুঝছি।


(টিএলডিআর উত্তর: বিবৃতিটি অন্তর্ভুক্ত করুন: cin.sync_with_stdio(false)বা fgetsপরিবর্তে কেবল ব্যবহার করুন।

টিএলডিআর ফলাফল: আমার প্রশ্নের নীচে সমস্ত দিকে স্ক্রোল করুন এবং টেবিলটি দেখুন))


সি ++ কোড:

#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 (স্নো চিতা) এবং লিনাক্স ২.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

ক্ষুদ্র বেঞ্চমার্ক সংযোজন এবং পুনর্নির্মাণ

সম্পূর্ণতার জন্য, আমি ভেবেছিলাম যে আমি একই বক্সে একই ফাইলের পড়ার গতিটি মূল (সিঙ্ক করা) সি ++ কোড সহ আপডেট করব। আবার এটি একটি দ্রুত ডিস্কে 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
@ জেজেসি: আমি দুটি সম্ভাবনা দেখতে পাচ্ছি (ধরে নিলাম আপনি ডেভিডের প্রস্তাবিত ক্যাচিং সমস্যাটি সরিয়ে দিয়েছেন): ১) <iostream>পারফরম্যান্স সাফল্য অর্জন করবে। এটি প্রথমবার হয় না। 2) পাইথন লুপের জন্য ডেটা অনুলিপি না করার জন্য যথেষ্ট চালাক কারণ আপনি এটি ব্যবহার করেন না। আপনি ব্যবহার করার চেষ্টা করে পরীক্ষা করতে পারেন scanfএবং একটি char[]। বিকল্পভাবে আপনি লুপটি পুনরায় লেখার চেষ্টা করতে পারেন যাতে স্ট্রিং দিয়ে কিছু করা হয় (উদাহরণস্বরূপ 5 তম অক্ষরটি রাখুন এবং ফলস্বরূপ এটিকে সংলগ্ন করুন)।
জেএন

15
সমস্যাটি স্টডিওর সাথে সিঙ্ক্রোনাইজেশন - আমার উত্তর দেখুন।
ভন ক্যাটো

19
যেহেতু কেউ কেন আপনি সি ++ দিয়ে অতিরিক্ত লাইন পান তা উল্লেখ করেননিcin.eof() বলে মনে হচ্ছে: এর বিরুদ্ধে পরীক্ষা দিবেন না !! getline'If`' স্টেটমেন্টে কলটি রাখুন ।
Xeo

21
wc -lদ্রুত কারণ এটি একবারে একাধিক লাইনের স্ট্রিমটি পড়ে (এটি fread(stdin)/memchr('\n')সংমিশ্রণ হতে পারে )। পাইথনের ফলাফল একই মাত্রায় যেমন ক্রম যেমনwc-l.py
jfs

উত্তর:


1644

ডিফল্টরূপে, cinস্টডিওয়ের সাথে সিঙ্ক্রোনাইজ করা হয়, যার ফলে এটি কোনও ইনপুট বাফারিং এড়ায়। আপনি যদি এটিকে আপনার মূল শীর্ষে যুক্ত করেন তবে আপনার আরও ভাল পারফরম্যান্স দেখতে হবে:

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। এই টাস্কটি প্রায় অবশ্যই আমি / হে আবদ্ধ এবং std::stringসি ++ তে অবজেক্ট তৈরি করার খরচ বা <iostream>নিজেই ব্যবহার করার ব্যয় সম্পর্কে অনেক বেশি FUD রয়েছে ।
কার্ল নচেটেল

51
হ্যাঁ, লুপটি কোডটি অজগরকে ছাড়িয়ে যাওয়ার জন্য অবিলম্বে আমার মূলের উপরে above আমি চূড়ান্ত সম্পাদনা হিসাবে ফলাফল পোস্ট করতে চলেছি। আবার ধন্যবাদ!
জেজেসি

6
হ্যাঁ, এটি আসলে কাউট, সেরার এবং ক্লোজের ক্ষেত্রেও প্রযোজ্য।
ভন ক্যাটো

2
কাউট, সিন, সেরার এবং দ্রুত বাঁধা তৈরি করতে, এই পদ্ধতিতে স্ট্যান্ড করুন :: আইওএস_বেস :: সিঙ্ক_উইথ_স্টডিও (মিথ্যা);
01100110

56
দ্রষ্টব্য এটি sync_with_stdio()একটি স্ট্যাটিক সদস্য ফাংশন, এবং কোনও স্ট্রিম অবজেক্টে এই ফাংশনটির কল (যেমন cin) সমস্ত স্ট্যান্ডার্ড আইস্ট্রিম অবজেক্টের জন্য সিঙ্ক্রোনাইজেশন চালু বা বন্ধ করে দেয়।
জন জুইনক

170

কৌতূহলের বাইরে আমি হুডের নীচে কী ঘটে তা একবার দেখেছি এবং প্রতিটি পরীক্ষায় আমি 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

159

আমি এখানে কয়েক বছর পিছিয়ে আছি, কিন্তু:

মূল পোস্টের '4/5/6 সম্পাদনা করুন' তে আপনি নির্মাণটি ব্যবহার করছেন:

$ /usr/bin/time cat big_file | program_to_benchmark

এটি বেশ কয়েকটি ভিন্ন পদ্ধতিতে ভুল:

  1. আপনি প্রকৃতপক্ষে আপনার বেঞ্চমার্ক নয়, `ক্যাট`কে কার্যকর করার সময় নির্ধারণ করছেন। 'ব্যবহারকারী' এবং 'সিজ' সিপিইউ ব্যবহারটি `সময়` দ্বারা প্রদর্শিত হয়` ক্যাট` এর, এটি আপনার বেঞ্চমার্কযুক্ত প্রোগ্রাম নয়। আরও খারাপ, 'আসল' সময়ও যথাযথভাবে সঠিক নয়। আপনার স্থানীয় ওএসে `ক্যাট` এবং পাইপলাইনগুলির প্রয়োগের উপর নির্ভর করে, এটি সম্ভবত সম্ভব যে` ক্যাট` একটি চূড়ান্ত দৈত্য বাফার লিখে এবং পাঠক প্রক্রিয়াটি এর কাজ শেষ করার অনেক আগেই বেরিয়ে আসে।

  2. "বিড়াল" ব্যবহার অপ্রয়োজনীয় এবং বাস্তবে প্রতিরক্ষামূলক; আপনি চলন্ত অংশ যুক্ত করছেন। যদি আপনি পর্যাপ্ত পুরানো সিস্টেমে থাকেন (যেমন একক সিপিইউ সহ এবং কম্পিউটারের নির্দিষ্ট প্রজন্মের মধ্যে - সিপিইউর তুলনায় I / O দ্রুত) - কেবলমাত্র "ক্যাট" চলছে তা ফলাফলকে রঙিন করতে পারে। ইনপুট এবং আউটপুট বাফারিং এবং অন্যান্য প্রক্রিয়াকরণ `ক্যাট` যা কিছু করতে পারে আপনিও তার অধীনে। (আমি সম্ভবত র্যান্ডাল শোয়ার্জ থাকিলে এটি সম্ভবত আপনাকে 'বেড়ালের ব্যবহারের অবধি' পুরষ্কার অর্জন করবে )

আরও ভাল নির্মাণ হবে:

$ /usr/bin/time program_to_benchmark < big_file

এই বিবৃতিতে এটি শেল যা আপনার প্রোগ্রামে (ভাল, আসলে `সময়` যা পরে আপনার প্রোগ্রামটিকে একটি সাবপ্রসেস হিসাবে কার্যকর করে) একটি ইতিমধ্যে-খোলা ফাইল বর্ণনাকারী হিসাবে প্রেরণ করে এটি বড়_ফাইলে খোলে। ফাইল রিডিংয়ের 100% হ'ল আপনি যে প্রোগ্রামটিকে বেনমার্ক করার চেষ্টা করছেন তার কঠোরভাবে দায়বদ্ধ। এটি আপনাকে উদ্দীপনাজনিত জটিলতা ছাড়াই এর কার্য সম্পাদনের সত্যিকারের পঠন দেয়।

আমি দুটি সম্ভাব্য, তবে আসলে ভুল, 'ফিক্স' উল্লেখ করব যা এগুলিও বিবেচিত হতে পারে (তবে আমি তাদের আলাদাভাবে 'সংখ্যা' বলছি কারণ এটি মূল পোস্টে ভুল ছিল না):

উ: আপনি কেবলমাত্র আপনার প্রোগ্রামের সময় নির্ধারণের মাধ্যমে এটি 'সমাধান' করতে পারেন:

$ cat big_file | /usr/bin/time program_to_benchmark

বি বা পুরো পাইপলাইন সময় দ্বারা:

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

# 2 হিসাবে একই কারণে এগুলি ভুল: তারা এখনও অযথা `ক্যাট` ব্যবহার করছে। আমি কয়েকটি কারণে তাদের উল্লেখ করছি:

  • তারা POSIX শেলের I / O পুনঃনির্দেশ সুবিধার সাথে পুরোপুরি আরামদায়ক নয় এমন লোকদের জন্য আরও 'প্রাকৃতিক'

  • সেখানে ক্ষেত্রে যেখানে `cat` হতে পারে হয় প্রয়োজন (যেমন: ফাইল পড়তে হবে অ্যাক্সেস করতে সুবিধা কিছু বাছাই প্রয়োজন, এবং আপনি benchmarked করা প্রোগ্রাম সেই বিশেষ সুযোগ দান করতে চাই না:` উবুন্টু বিড়াল / dev / sda | / usr / বিন / সময় my_compression_test - না-আউটপুট`)

  • বাস্তবে , আধুনিক মেশিনে যুক্ত `পাইপলাইন cat` সম্ভবত কোনো সত্যিকারের ফল হয়

তবে আমি শেষ কথাটি কিছুটা দ্বিধা নিয়ে বলি। যদি আমরা 'এডিট 5' এ শেষ ফলাফলটি পরীক্ষা করি -

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

- এটি দাবি করে যে `ক্যাট` পরীক্ষার সময় 74৪% সিপিইউ গ্রাস করেছিল; এবং প্রকৃতপক্ষে 1.34 / 1.83 প্রায় 74%। সম্ভবত একটি রান:

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

শুধুমাত্র বাকি .49 সেকেন্ড গ্রহণ করা হবে! সম্ভবত এটি নয়: এখানে `ক্যাটাকে রিড () সিস্টেম কল (বা সমতুল্য) যা 'ডিস্ক' (আসলে বাফার ক্যাশে) থেকে ফাইলটি স্থানান্তরিত করেছিল, সেইসাথে পাইপগুলি তাদের` ডাব্লুসিএইতে পৌঁছে দেওয়ার জন্য লিখেছিল ` সঠিক পরীক্ষাটি এখনও সেই পাঠ্য () কলগুলিতে করতে হত; কেবল পাইপ-থেকে পাইপ এবং পঠন-থেকে পাইপ কলগুলি সংরক্ষণ করা যেতে পারে এবং সেগুলি বেশ সস্তা হওয়া উচিত।

তবুও, আমি পূর্বাভাস দিচ্ছি আপনি বিড়াল ফাইল | এর মধ্যে পার্থক্যটি পরিমাপ করতে সক্ষম হবেন wc -l` এবং `wc -l <file` এবং একটি লক্ষণীয় (2-অঙ্কের শতাংশ) পার্থক্য সন্ধান করুন। ধীর পরীক্ষার প্রত্যেকটি পরম সময়ে একই ধরণের জরিমানা প্রদান করবে; যা এর বৃহত্তর মোট সময়ের একটি ছোট ভগ্নাংশের সমান হবে।

আসলে আমি লিনাক্স 3.13 (উবুন্টু 14.04) সিস্টেমে 1.5 গিগাবাইট ফাইলের জঞ্জালের সাথে কিছু দ্রুত পরীক্ষা করেছিলাম, এই ফলাফলগুলি পেয়েছি (এগুলি আসলে 'সেরা 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)

লক্ষ্য করুন যে দুটি পাইপলাইন ফলাফল বাস্তব ওয়াল-ক্লক সময়ের চেয়ে বেশি সিপিইউ সময় (ব্যবহারকারী + সিস) নিয়েছে বলে দাবি করেছে। এটি কারণ আমি শেল (বাশ) এর অন্তর্নির্মিত 'সময়' কমান্ডটি ব্যবহার করছি, যা পাইপলাইনের জ্ঞাত; এবং আমি একটি মাল্টি-কোর মেশিনে আছি যেখানে পাইপলাইনে পৃথক প্রক্রিয়াগুলি পৃথক কোর ব্যবহার করতে পারে, সিপিইউ সময়টি রিয়েলটাইমের চেয়ে দ্রুত জমে। / ইউএসআর / বিন / সময় ব্যবহার করে আমি রিয়েলটাইমের চেয়ে সিপিইউ সময় আরও কম দেখি - এটি দেখায় যে এটি কেবলমাত্র একক পাইপলাইন উপাদানটিকে তার কমান্ড লাইনে পাস করতে পারে। এছাড়াও, শেলের আউটপুটটি মিলি সেকেন্ড দেয় যখন / ইউএসআর / বিন / সময় কেবল এক সেকেন্ডের শততম দেয়।

সুতরাং `wc -l` এর দক্ষতার স্তরে,` ক্যাট` একটি বিশাল পার্থক্য করে: 409/283 = 1.453 বা 45.3% বেশি রিয়েলটাইম, এবং 775/280 = 2.768, বা আরও একটি বড় 177% সিপিইউ ব্যবহৃত হয়! আমার এলোমেলো সময়ে এটি ছিল-সময়-টেস্ট বাক্সে।

আমার যুক্ত করা উচিত যে পরীক্ষার এই শৈলীর মধ্যে কমপক্ষে আরও একটি উল্লেখযোগ্য পার্থক্য রয়েছে এবং আমি এটি বলতে পারি না এটি লাভ বা দোষ কিনা; আপনার নিজের এটি সিদ্ধান্ত নিতে হবে:

আপনি যখন `ক্যাট বিগ_ফায়াল চালাবেন | / usr / bin / সময় my_program`, আপনার প্রোগ্রামটি পাইপের কাছ থেকে ইনপুট গ্রহণ করছে, অবিকল গতিতে `ক্যাট` দ্বারা প্রেরণ করা হয়েছে, এবং খণ্ডগুলিতে` ক্যাট by রচিত চেয়ে বড় কোনও নয় `

আপনি যখন `/ usr / bin / সময় my_pramram <big_file` চালান, তখন আপনার প্রোগ্রামটি আসল ফাইলটিতে একটি মুক্ত ফাইল বর্ণনাকারী গ্রহণ করে। আপনার প্রোগ্রাম - বা অনেক ক্ষেত্রে যে ভাষায় এটি লেখা হয়েছিল তার I / O লাইব্রেরিগুলি নিয়মিত ফাইলের উল্লেখ করে কোনও ফাইল বর্ণনাকারীর সাথে উপস্থাপিত হলে বিভিন্ন পদক্ষেপ নিতে পারে। এটি স্পষ্টভাবে পঠিত (2) সিস্টেম কলগুলি ব্যবহার না করে তার ঠিকানা স্পেসে ইনপুট ফাইলটি ম্যাপ করতে এমএমএপি (2) ব্যবহার করতে পারে। এই বিভেদগুলি আপনার বেঞ্চমার্ক ফলাফলগুলিতে the ক্যাট` বাইনারি চালানোর স্বল্প ব্যয়ের চেয়ে আরও বড় প্রভাব ফেলতে পারে।

দুটি প্রোগ্রামের মধ্যে একই প্রোগ্রাম যদি উল্লেখযোগ্যভাবে আলাদাভাবে সম্পাদন করে তবে অবশ্যই এটি একটি আকর্ষণীয় মানদণ্ডের ফলাফল। এটা যে দেখায়, প্রকৃতপক্ষে, প্রোগ্রাম বা তার ইনপুট / আউটপুট লাইব্রেরি হয় আকর্ষণীয় কিছু করছেন, mmap ব্যবহার করার মত ()। সুতরাং অনুশীলনে উভয়ভাবে মাপদণ্ড চালানো ভাল হতে পারে; সম্ভবত "বিড়াল" চালানোর ব্যয়টি "ক্ষমা" করার জন্য কিছু ক্ষুদ্র উপাদান দ্বারা by ক্যাটাল ফলাফলকে ছাড় দেওয়া হচ্ছে।


26
বাহ, বেশ অন্তর্দৃষ্টিপূর্ণ ছিল! যদিও আমি সচেতন ছিলাম যে বিড়াল প্রোগ্রামগুলির স্টিনে ইনপুট খাওয়ানোর জন্য অপ্রয়োজনীয় এবং <শেল পুনর্নির্দেশকে পছন্দ করা হয়, আমি সাধারণত পূর্ব পদ্ধতিটি চোখের সামনে সংরক্ষণ করে এমন ডেটার বাম থেকে ডান প্রবাহের কারণে বিড়ালের সাথে আটকে থাকি যখন আমি পাইপলাইনগুলি সম্পর্কে বিতর্ক করি। এই ধরনের ক্ষেত্রে পারফরম্যান্সের পার্থক্যগুলি আমি নগণ্য বলে মনে করেছি। তবে, বেলা, আপনারা আমাদের শিক্ষিত করার জন্য আমি তাদের প্রশংসা করি।
জেজেসি

11
পুনর্নির্দেশটি শেল কমান্ড লাইন থেকে প্রাথমিক পর্যায়ে পার্স করা হয়, এটি আপনাকে বাম থেকে ডান প্রবাহের আরও আকর্ষণীয় চেহারা দেয় তবে এগুলির মধ্যে একটি করতে সক্ষম করে: $ < big_file time my_program $ time < big_file my_program এটি কোনও পসিক্স শেলটিতে কাজ করা উচিত (উদাহরণস্বরূপ sh csh নয়) `এবং আমি ot rc` এর মতো এক্সোটিকা সম্পর্কে নিশ্চিত নই:)
বেলা লুবকিন

6
আবার একই সময়ে `ক্যাট` বাইনারি চলমান কারণে সম্ভবত উদ্বেগজনক বর্ধনশীল পারফরম্যান্স পার্থক্য বাদ দিয়ে, আপনি পরীক্ষার অধীনে প্রোগ্রামটির ইনপুট ফাইল এমএম্যাপ করতে সক্ষম হওয়ার সম্ভাবনাটি ছেড়ে দিচ্ছেন। এটি ফলাফলগুলিতে গভীর পার্থক্য করতে পারে। আইডিয়োমটি কেবল 'ফাইল থেকে ইনপুট লাইন' ব্যবহার করে, বিভিন্ন ভাষায়, আপনি নিজেই মাপদণ্ড লিখেছিলেন তা এমনকি সত্য। এটি তাদের বিভিন্ন আই / ও লাইব্রেরির বিস্তারিত কাজের উপর নির্ভর করে।
বেলা লুবকিন

2
পার্শ্ব নোট: বাশের বিল্টিন timeপ্রথম প্রোগ্রামটির পরিবর্তে পুরো পাইপলাইনটি পরিমাপ করছে। time seq 2 | while read; do sleep 1; doneপ্রিন্ট 2 সেকেন্ড, /usr/bin/time seq 2 | while read; do sleep 1; done0 সেকেন্ড প্রিন্ট করে।
ফোকল

1
@ ফোকল - হ্যাঁ, << লক্ষ্য করুন যে দুটি পাইপলাইনের ফলাফল [বাশ) -র বিল্ট-ইন 'টাইম' কমান্ডের চেয়ে রিয়েলটাইম [বেশি] সিপিইউ [বেশি] দেখায়; ... / usr / বিন / সময় ... কেবলমাত্র কমান্ড লাইনে একক পাইপলাইন উপাদান এটিতে যেতে পারে। >> '
বেলা লুবকিন

90

আমি ম্যাকের উপর g ++ ব্যবহার করে আমার কম্পিউটারে আসল ফলাফলটি পুনরুত্পাদন করেছি।

whileলুপটি পাইথন সংস্করণটির সাথে ইনলাইন আনার ঠিক আগে সি -+ সংস্করণে নিম্নলিখিত বিবৃতিগুলি যুক্ত করা :

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

sync_with_stdio গতি 2 সেকেন্ডে উন্নত করেছে এবং একটি বৃহত্তর বাফার সেট করে এটি এটিকে 1 সেকেন্ডে নামিয়েছে।


5
আপনি আরও দরকারী তথ্য পেতে বিভিন্ন বাফার আকার চেষ্টা করতে চাইতে পারেন। আমার সন্দেহ হয় আপনি দ্রুত হ্রাসজনক আয় দেখবেন।
কার্ল নচেটেল 21'12

8
আমার জবাবে আমি খুব তাড়াহুড়ো করেছিলাম; ডিফল্ট ব্যতীত অন্য কিছুতে বাফার আকারটি সেট করা কোনও প্রশংসনীয় পার্থক্য তৈরি করতে পারেনি।
করুণস্কি

109
আমি স্ট্যাকের উপর 1MB বাফার সেট আপ করা এড়াতে চাই। এটি স্ট্যাকওভারফ্লো হতে পারে (যদিও আমি ধারণা করি এটি সম্পর্কে বিতর্ক করার জন্য এটি ভাল জায়গা!)
ম্যাথিউ এম।

11
ম্যাথিউ, ম্যাক ডিফল্টরূপে একটি 8 এমবি প্রক্রিয়া স্ট্যাক ব্যবহার করে। লিনাক্স প্রতি থ্রেড ডিফল্ট 4MB ব্যবহার করে, আইআইআরসি। তুলনামূলকভাবে অগভীর স্ট্যাকের গভীরতার সাথে ইনপুটকে রূপান্তর করে এমন কোনও প্রোগ্রামের জন্য 1 এমবি তেমন সমস্যা নয়। আরও গুরুত্বপূর্ণ, যদিও, স্টাড :: সিফার স্ট্যাকটি ট্র্যাশ করবে যদি বাফার সুযোগের বাইরে চলে যায়।
SEK

22
@SEK উইন্ডোজ ডিফল্ট স্ট্যাকের আকার 1MB।
É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)) in

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 এর চেয়ে বেশি মারধর করে।


আপনি আরও দ্রুত যে এর চেয়ে একটি ক্ষুদ্র কাস্টম কিন্তু সম্পূর্ণরূপে সহজবোধ্য সি প্রোগ্রাম যা iteratively এই পারেন unbuffered তোলে সঙ্গে পেতে পারেন readদৈর্ঘ্যের একটি স্ট্যাটিক বাফারের মধ্যে syscalls BUFSIZEবা সমমানের সংশ্লিষ্ট মাধ্যমে mmapa la যে বাফার কাউন্টিং নতুন লাইন মাধ্যমে syscalls, এবং তারপর চাবুক for (char *cp = buf; *cp; cp++) count += *cp == "\n"BUFSIZEআপনার সিস্টেমে আপনাকে টিউন করতে হবে, তবে, কোন স্টডিও আপনার জন্য ইতিমধ্যে করেছে। তবে সেই forলুপটি আপনার বাক্সের হার্ডওয়্যারের জন্য দুর্দান্তভাবে চিৎকার-দ্রুত একত্রিতকারী ভাষার নির্দেশাবলীর জন্য সঙ্কলন করা উচিত।
tchrist

3
কাউন্টি_আইএফ এবং একটি ল্যাম্বদাও "দুর্দান্তভাবে চেঁচামেচা-দ্রুত সমাবেশকারী" এ সংকলন করে।
পিটার ২

17

যাইহোক, সি ++ সংস্করণের জন্য লাইন গণনাটি পাইথন সংস্করণের জন্য গণনার তুলনায় একটি বৃহত্তর কারণটি হল ইওফ পতাকাটি তখনই সেট হয়ে যায় যখন ইওফের বাইরে পড়ার চেষ্টা করা হয়। সুতরাং সঠিক লুপটি হ'ল:

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

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

70
সত্যই সঠিক লুপটি while (getline(cin, input_line)) line_count++;
হ'ল

2
@ জোনাথন ওয়াকলি আমি জানি যে আমি বেশ দেরি করে ফেলেছি, তবে ব্যবহার করছি ++line_count;এবং নেই line_count++;
বলেছেন মনিকা পুনরায় ইনস্টল করুন মনিকা

7
@ ওভাল যদি এটি কোনও পার্থক্য করে তবে আপনার সংকলকটিতে একটি বাগ রয়েছে। চলকটি একটি longএবং সংকলকটি বর্ধনের ফলাফল ব্যবহার করা হয়নি তা জানাতে যথেষ্ট সক্ষম। যদি এটি পোস্ট-ইনক্রিমেন্ট এবং প্রিনক্রেনমেন্টের জন্য অভিন্ন কোড তৈরি না করে তবে এটি ভেঙে গেছে।
জোনাথন ওয়েকেলি

2
প্রকৃতপক্ষে কোনও শালীন সংকলক একটি পোস্ট-ইনক্রিমেন্টের অপব্যবহার সনাক্ত করতে সক্ষম করবে এবং এর পরিবর্তে প্রাক-বর্ধিতকরণ দ্বারা এটি প্রতিস্থাপন করতে সক্ষম হবে, তবে সংকলকগুলির প্রয়োজন নেই । সুতরাং না, সংকলক বিকল্পটি সম্পাদন না করলেও এটি ভাঙা হয়নি। ++line_count;line_count++;
তদ্ব্যতীত

1
@ ভ্যালসেসরেইনস্টেটমোনিকা এই নির্দিষ্ট উদাহরণে, কেন একজনকে পছন্দ করা হবে? ফলাফলটি এখানে কোনওভাবেই ব্যবহৃত হয় না, তাই এটি 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 ব্যবহার করছি।
জেজেসি

10
সি ++ সংস্করণ ফিক্স করার পরে, এই স্টডিও সংস্করণটি আমার কম্পিউটারে সি ++ আইস্ট্রিমেজ সংস্করণটির তুলনায় যথেষ্ট ধীর। (3 সেকেন্ড বনাম 1 সেকেন্ড)
করুণস্কি

10

ঠিক আছে, আমি দেখতে পাচ্ছি যে আপনার দ্বিতীয় সমাধানে আপনি সরে cinগিয়েছিলেন scanf, এটিই প্রথম পরামর্শ যা আমি আপনাকে তৈরি করতে যাচ্ছি (cin is sloooooooooooow)। এখন, আপনি যদি থেকে সুইচ scanfকরতে fgets, আপনি কর্মক্ষমতা অন্য বুস্ট দেখতে হবে:fgets স্ট্রিং ইনপুট জন্য দ্রুততম সি ++ ফাংশন।

বিটিডাব্লু, সেই সিঙ্ক জিনিসটি সম্পর্কে জানত না, দুর্দান্ত। তবে আপনার এখনও চেষ্টা করা উচিতfgets


2
ব্যতীত fgetsভুল হবে ভালোই বড় লাইনের জন্য (লাইন গন্য নিরিখে এবং loops জুড়ে বিভাজন রেখা পরিপ্রেক্ষিতে আপনি আসলে তাদের ব্যবহার করার জন্য প্রয়োজন হলে), অসম্পূর্ণ লাইন জন্য অতিরিক্ত চেক ছাড়া (এবং ক্ষতিপূরণ এটা অকারণে বড় বাফার বণ্টন জড়িত জন্য প্রয়াস , যেখানে std::getlineপ্রকৃত ইনপুট নির্বিঘ্নে মেলে পুনঃস্থাপন পরিচালনা করে)। দ্রুত এবং ভুল সহজ, তবে এটি "সামান্য ধীর, তবে সঠিক" ব্যবহার করা প্রায় সর্বদা তার পক্ষে মূল্যবান, যা sync_with_stdioআপনাকে বন্ধ করে দেয় ।
শ্যাডোর্যাঞ্জার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.