সি এবং সি ++ এর প্রায় অভিন্ন কোডের মধ্যে সম্পাদনের সময় বড় পার্থক্য (x9)


85

আমি www.spoj.com থেকে এই অনুশীলনটি সমাধান করার চেষ্টা করছিলাম: FCTRL - ফ্যাক্টরিয়াল orial

আপনাকে সত্যিই এটি পড়তে হবে না, আপনি যদি আগ্রহী হন তবে এটি করুন :)

প্রথমে আমি এটি সি ++ এ প্রয়োগ করেছি (এটি আমার সমাধান):

#include <iostream>
using namespace std;

int main() {
    unsigned int num_of_inputs;
    unsigned int fact_num;
    unsigned int num_of_trailing_zeros;

    std::ios_base::sync_with_stdio(false); // turn off synchronization with the C library’s stdio buffers (from https://stackoverflow.com/a/22225421/5218277)

    cin >> num_of_inputs;

    while (num_of_inputs--)
    {
        cin >> fact_num;

        num_of_trailing_zeros = 0;

        for (unsigned int fives = 5; fives <= fact_num; fives *= 5)
            num_of_trailing_zeros += fact_num/fives;

        cout << num_of_trailing_zeros << "\n";
    }

    return 0;
}

আমি এটি g ++ 5.1 এর সমাধান হিসাবে আপলোড করেছি

ফল: টাইম 0.18 মেম 3.3M সি ++ কার্যকরকরণের ফলাফল

কিন্তু তারপরে আমি কিছু মন্তব্য দেখেছি যা দাবি করেছে যে তাদের সময় কার্যকর করা 0.1 এর চেয়ে কম ছিল। যেহেতু আমি মনে করি না পারে দ্রুত অ্যালগরিদম আমি একই কোড বাস্তবায়ন করার চেষ্টা সি :

#include <stdio.h>

int main() {
    unsigned int num_of_inputs;
    unsigned int fact_num;
    unsigned int num_of_trailing_zeros;

    scanf("%d", &num_of_inputs);

    while (num_of_inputs--)
    {
        scanf("%d", &fact_num);

        num_of_trailing_zeros = 0;

        for (unsigned int fives = 5; fives <= fact_num; fives *= 5)
            num_of_trailing_zeros += fact_num/fives;

        printf("%d", num_of_trailing_zeros);
        printf("%s","\n");
    }

    return 0;
}

আমি এটি জিসিসি 5.1 এর সমাধান হিসাবে আপলোড করেছি

এবার ফলাফল ছিল: সময় 0.02 মেমি 2.1M সি কার্যকর করার ফলাফল

কোডটি এখন প্রায় একই , আমি সি লাইব্রেরির স্টিডিও বাফারগুলির সাথে সিঙ্ক্রোনাইজেশন বন্ধ করার জন্য এখানেstd::ios_base::sync_with_stdio(false); পরামর্শ হিসাবে সি ++ কোড যুক্ত করেছি । আমিও বিভক্ত করার দ্বিগুণ কলের জন্য ক্ষতিপূরণ মধ্যে ।printf("%d\n", num_of_trailing_zeros);printf("%d", num_of_trailing_zeros); printf("%s","\n");operator<<cout << num_of_trailing_zeros << "\n";

তবে আমি এখনও সি 9 বনাম সি ++ কোডে এক্স 9 এর আরও ভাল পারফরম্যান্স এবং মেমরির কম ব্যবহার দেখতে পেয়েছি ।

তা কেন?

সম্পাদনা

আমি স্থির unsigned longকরার unsigned intসি কোডে। এটি হওয়া উচিত ছিল unsigned intএবং উপরে প্রদর্শিত ফলাফলগুলি নতুন ( unsigned int) সংস্করণের সাথে সম্পর্কিত ।


31
সি ++ স্ট্রিমগুলি ডিজাইন দ্বারা অত্যন্ত ধীর। কারণ ধীর এবং অবিচলিত দৌড় প্রতিযোগিতায় জয়লাভ করে। : পি ( আমার আগুন
জ্বলানোর

7
স্লোনেসটি সুরক্ষা বা অভিযোজনযোগ্যতা থেকে আসে না। এটি সমস্ত স্ট্রিম পতাকাগুলির সাথে ওভারডিজাইন হয়েছে।
করলি হরভাথ

8
@ অ্যালেক্সলপ std::ostringstreamআউটপুট সংগ্রহ করতে a ব্যবহার করে এবং std::cout শেষে একবারে এটি পাঠানো সময়কে 0.02 এ নেমে আসে। std::coutএকটি লুপ ব্যবহার করা তাদের পরিবেশে খুব ধীর এবং এটিকে উন্নত করার কোনও সহজ উপায় আছে বলে আমি মনে করি না।
ব্লাস্টফর্নেস

6
এই সময়টি আদর্শের সাহায্যে প্রাপ্ত হয়েছিল তা নিয়ে কি আর কেউ উদ্বিগ্ন নয়?
iljarn

6
@ ওলাফ: আমি ভয় করি যে আমি দ্বিমত পোষণ করছি, এই ধরণের প্রশ্নটি নির্বাচিত সমস্ত ট্যাগের জন্য প্রসঙ্গটি খুব বেশি। সি এবং সি ++ সাধারণভাবে পর্যাপ্তরূপে যে পারফরম্যান্সের মধ্যে এই ধরনের পার্থক্য একটি ব্যাখ্যাটির জন্য প্রার্থনা করে। আমি খুশি যে আমরা এটি পেয়েছি। সম্ভবত GNU libc ++ এর ফলস্বরূপ উন্নতি করা উচিত।
chqrlie

উত্তর:


56

উভয় প্রোগ্রামই ঠিক একই কাজ করে। তারা একই সঠিক অ্যালগরিদম ব্যবহার করে এবং এর কম জটিলতার কারণে তাদের কর্মক্ষমতা বেশিরভাগই ইনপুট এবং আউটপুট হ্যান্ডলিংয়ের দক্ষতার সাথে আবদ্ধ।

scanf("%d", &fact_num);একদিকে এবং অন্যদিকে ইনপুট স্ক্যান করা cin >> fact_num;কোনওভাবেই খুব ব্যয়বহুল বলে মনে হয় না। প্রকৃতপক্ষে এটি সি ++ তে কম ব্যয়বহুল হওয়া উচিত কারণ সংকলনের সময় রূপান্তরকরণের ধরণটি জানা যায় এবং সঠিক পার্সার সরাসরি সি ++ সংকলক দ্বারা আহ্বান করা যেতে পারে। আউটপুট জন্য একই হোল্ড। এমনকি আপনি আলাদা কল করার জন্য একটি বিন্দুও লিখেছেন printf("%s","\n");, তবে সি সংকলককে কল হিসাবে এটি সংকলন করতে যথেষ্ট ভাল putchar('\n');

সুতরাং I / O এবং গণনা উভয়ের জটিলতার দিকে তাকানো, সি ++ সংস্করণটি সি সংস্করণের চেয়ে দ্রুত হওয়া উচিত।

সম্পূর্ণরূপে বাফারিং অক্ষম করা stdoutসি বাস্তবায়নকে সি ++ সংস্করণের চেয়ে ধীর গতিতে ধীর করে দেয়। fflush(stdout);শেষের পরে আলেক্সলপের আরও একটি পরীক্ষাprintf সি ++ সংস্করণ হিসাবে একইরকম পারফরম্যান্স দেয়। এটি সম্পূর্ণরূপে বাফারিং অক্ষম করার মতো ধীর নয় কারণ আউটপুট একবারে এক বাইটের পরিবর্তে ছোট খণ্ডে সিস্টেমে লেখা থাকে।

এটি আপনার সি ++ লাইব্রেরিতে একটি নির্দিষ্ট আচরণের দিকে ইঙ্গিত করে বলে মনে হচ্ছে: আমি আপনার সিস্টেমের প্রয়োগটি সন্দেহ করি cinএবং ইনপুট থেকে অনুরোধ coutকরা coutহলে আউটপুট ফ্লাশ করে cin। কিছু সি লাইব্রেরি এটিও করে তবে সাধারণত টার্মিনালে এবং পড়ার সময় / পড়ার সময়। Www.spoj.com সাইট দ্বারা করা বেঞ্চমার্কিং সম্ভবত ফাইলগুলিতে এবং ইনপুট এবং আউটপুটটিকে পুনঃনির্দেশ করে।

অ্যালেক্সলপ আরও একটি পরীক্ষা করেছিলেন: ভেক্টরে একবারে সমস্ত ইনপুট পড়া এবং পরবর্তীতে সমস্ত আউটপুট গণনা করা এবং লেখার ফলে কেন সি ++ সংস্করণ এত ধীর হয় তা বুঝতে সহায়তা করে। এটি সি সংস্করণে কর্মক্ষমতা বৃদ্ধি করে, এটি আমার বক্তব্য প্রমাণ করে এবং সি ++ ফর্ম্যাটিং কোডের উপর সন্দেহ সরিয়ে দেয়।

ব্লাস্টফর্নাসের আরেকটি পরীক্ষা, সমস্ত আউটপুট একটি সংরক্ষণ করে std::ostringstreamএবং ফ্লাশ করে যে একটি বিস্ফোরণে, সি সি ++ এর পারফরম্যান্সটিকে বেসিক সি সংস্করণে উন্নত করে। কিউইডি

ইনপুট থেকে cinআউটপুটকে ইন্টারলেসিং করা coutস্ট্রিম বাফারিং স্কিমকে পরাস্ত করে খুব অকার্যকর আই / ও হ্যান্ডলিংয়ের কারণ বলে মনে হচ্ছে। 10 এর গুণক দ্বারা কর্মক্ষমতা হ্রাস করা।

PS: আপনার অ্যালগরিদম এর জন্য ভুল fact_num >= UINT_MAX / 5কারণ fives *= 5এটি ওভারফ্লো হয়ে যাবে এবং এটি হয়ে ওঠার আগেই তার চারপাশে মোড়ানো হবে > fact_num। আপনি উপার্জন দ্বারা এই সংশোধন করতে পারেন fivesএকটি unsigned longঅথবা একটি unsigned long longযদি এই ধরনের এক চেয়ে বড় unsigned int%uহিসাবে ব্যবহার করুনscanfফর্ম্যাট । আপনি ভাগ্যবান www.spoj.com এ ছেলেরা তাদের মানদণ্ডগুলিতে খুব কঠোর নয়।

সম্পাদনা: পরে ভিটাউক্সের দ্বারা ব্যাখ্যা করা হিসাবে, এই আচরণটি সত্যই সি ++ স্ট্যান্ডার্ড দ্বারা বাধ্যতামূলক। ডিফল্ট cinহিসাবে আবদ্ধ হয় cout। একটি ইনপুট অপারেশন cinযার জন্য ইনপুট বাফারটিকে রিফিলিং প্রয়োজন coutমুলতুবি আউটপুট ফ্লাশ করে। ও.পি. এর বাস্তবায়নে নিয়মিতভাবে cinফ্লাশ coutলাগছে যা কিছুটা ওভারকিল এবং দৃশ্যমান অদক্ষ।

ইলিয়া পপোভ এটির জন্য একটি সহজ সমাধান সরবরাহ করেছেন: এ ছাড়াও আরও একটি যাদুকরী স্পেল ফেলে এটি cinথেকে মুক্ত coutকরা যেতে পারে std::ios_base::sync_with_stdio(false);:

cin.tie(nullptr);

আরও মনে রাখবেন যে লাইনের শেষ প্রান্তের std::endlপরিবর্তে ব্যবহার করার সময় এই ধরনের বাধ্যতামূলক ফ্লাশও ঘটে । আউটপুট লাইনটিকে আরও সি ​​++ এ মুছে ফেলা এবং নির্দোষ দেখতে একইভাবে পারফরম্যান্সকে হ্রাস করবে।'\n'coutcout << num_of_trailing_zeros << endl;


4
আপনি সম্ভবত স্ট্রিম ফ্লাশিং সম্পর্কে সঠিক। std::ostringstreamএকটিতে আউটপুট সংগ্রহ করা এবং শেষে একবারে আউটপুট সংগ্রহ করা সময়টিকে সি সংস্করণের সাথে সমতাতে নামিয়ে আনে।
ব্লাস্টফর্নেস

4
@ ডেভিডসি। র্যাঙ্কিন: আমি একটি অনুমানের উদ্রেক করলাম (সিউন পড়ার সময় চূড়ান্তভাবে ফুটে উঠেছে), এটি প্রমাণ করার জন্য একটি উপায় তৈরি করেছি, আলেক্সলপ এটি বাস্তবায়িত করেছে এবং এটি দৃ evidence়প্রত্যয়ী প্রমাণ দেয়, তবে ব্লাস্টফারনেস আমার বক্তব্য এবং তার পরীক্ষাগুলি প্রমাণ করার জন্য আলাদা উপায় নিয়ে এসেছিল সমানভাবে দৃinc়প্রত্যয়ী প্রমাণ দিন। আমি প্রমাণের জন্য এটি গ্রহণ করি, তবে অবশ্যই এটি পুরোপুরি আনুষ্ঠানিক প্রমাণ নয়, সি ++ উত্স কোডের দিকে তাকানো পারে।
chqrlie

4
আমি ostringstreamআউটপুটটি ব্যবহার করার চেষ্টা করেছি এবং এটি 0.02 কিউইডি সময় দিয়েছে :)। সংক্রান্ত fact_num >= UINT_MAX / 5, ভাল বিন্দু!
অ্যালেক্স লোপ

4
সমস্ত ইনপুট একটিতে সংগ্রহ করা vectorএবং তারপরে গণনাগুলি প্রক্রিয়া করা (ছাড়াই ostringstream) একই ফলাফল দেয়! সময় 0.02। উভয় সংমিশ্রণ vectorএবং ostringstreamএটি আরও উন্নত করে না একই সময় 0.02
অ্যালেক্স লুপ।

4
একটি সহজ ফিক্স যা এটি এমনকি এটির sizeof(int) == sizeof(long long)num_of_trailing_zeros += fact_num/fives;fivesif (fives > UINT_MAX / 5) break;
জন্যও কাজ করে

44

iostreamযখন আপনি উভয় ব্যবহার করেন cinএবং coutকল করেন তখন দ্রুততর করার আর একটি কৌশল

cin.tie(nullptr);

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

std::string name;
cout << "Enter your name:";
cin >> name;

এই ক্ষেত্রে আপনি নিশ্চিত করতে চান যে ইনপুটটির জন্য অপেক্ষা করা শুরু করার আগে প্রম্পটটি আসলে প্রদর্শিত হবে। আপনি উপরের লাইন দিয়ে ভেঙ্গে যে টাই, cinএবং coutস্বাধীন হয়ে।

যেহেতু সি ++ ১১, আইওস্ট্রিমে আরও ভাল পারফরম্যান্স অর্জনের আরও একটি উপায় হ'ল std::getlineএকসাথে এর ব্যবহার করা std::stoi:

std::string line;
for (int i = 0; i < n && std::getline(std::cin, line); ++i)
{
    int x = std::stoi(line);
}

এই উপায়ে পারফরম্যান্সে সি-স্টাইলের কাছাকাছি আসতে পারে, এমনকি ছাড়িয়ে যেতে পারে scanf। ব্যবহার করে getcharএবং বিশেষতgetchar_unlockedহস্তাক্ষর বিশ্লেষণের সাথে একত্রে আরও ভাল পারফরম্যান্স সরবরাহ করে।

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


4
ব্যাখ্যা এবং +1 সমাধান জন্য জন্য আপনাকে ধন্যবাদ, কিন্তু সঙ্গে আপনার প্রস্তাবিত বিকল্প std::readlineএবং std::stoiবৈশিষ্ট্যগুলি Ops কোড সমতূল্য নয়। উভয়ই cin >> x;এবং scanf("%f", &x);পিপড়া সাদা স্থানকে বিভাজক হিসাবে স্বীকার করে, একই লাইনে একাধিক সংখ্যা থাকতে পারে।
chqrlie

27

সমস্যাটি হ'ল সিপ্রেফারেন্সের উদ্ধৃতি দিয়ে :

স্ট্যান্ড :: সিন, আউটপুট থেকে স্টেড :: সেরার বা প্রোগ্রাম সমাপ্তি থেকে যে কোনও ইনপুট স্ট্যান্ড :: cout.flush () এ কল করতে বাধ্য করে

এটি পরীক্ষা করা সহজ: যদি আপনি প্রতিস্থাপন করেন

cin >> fact_num;

সঙ্গে

scanf("%d", &fact_num);

এবং এর জন্য একই cin >> num_of_inputsতবে coutআপনার সি ++ সংস্করণে (বা বরং আইওএসট্রিম সংস্করণ) সি'র মতোই একই পারফরম্যান্স পাবেন:

এখানে চিত্র বর্ণনা লিখুন

রাখলে একই রকম হয় cin তবে প্রতিস্থাপন করেন তবে

cout << num_of_trailing_zeros << "\n";

সঙ্গে

printf("%d", num_of_trailing_zeros);
printf("%s","\n");

একটি সহজ সমাধান মুক্ত করা coutএবংcin ইলিয়া পোপভ উল্লিখিত হিসাবে:

cin.tie(nullptr);

কিছু ক্ষেত্রে স্ট্যান্ডার্ড লাইব্রেরি বাস্তবায়নের জন্য কলটি বাদ দেওয়ার অনুমতি দেওয়া হয়েছে তবে সর্বদা তা নয়। সি ++ 14 27.7.2.1.3 (chqrlie ধন্যবাদ) এর একটি উদ্ধৃতি এখানে:

শ্রেণি মৌলিক_সম্পর্কিত :: সেন্ড্রি: প্রথমত, যদি is.tie () কোনও নাল পয়েন্টার না হয় তবে ফাংশনটি কল করে কোন সম্পর্কিত বহিরাগত সি স্ট্রিমের সাথে আউটপুট ক্রমকে সিঙ্ক্রোনাইজ করার জন্য is.tie () -> ফ্লাশ () কল করে। Is.tie () এর পুট অঞ্চলটি ফাঁকা থাকলে এই কলটি চাপা দেওয়া যায়। আরও একটি প্রয়োগের জন্য কলটি বিলম্বিত করার অনুমতি দেওয়া হয় যতক্ষণ না is.rdbuf () -> আন্ডারফ্লো () কল আসে না until যদি সেন্ড্রি অবজেক্টটি ধ্বংস হওয়ার আগে এমন কোনও কল না ঘটে তবে ফ্লাশের কলটি সম্পূর্ণরূপে মুছে ফেলা হতে পারে।


ব্যাখ্যার জন্য ধন্যবাদ. তবুও সি ++ 14 27.7.2.1.3 উদ্ধৃতি: শ্রেণীর মৌলিক_স্তরনা :: প্রেরণ : প্রথমত, যদি is.tie()নাল পয়েন্টার না হয় তবে ফাংশনটি is.tie()->flush()সম্পর্কিত কোনও বহিরাগত সি স্ট্রিমের সাথে আউটপুট ক্রমকে সিঙ্ক্রোনাইজ করতে বলে । এই কলটি চাপা দেওয়া যেতে পারে যদি এর পুটের জায়গাটি is.tie()খালি থাকে। আরও একটি বাস্তবায়ন কল কলটি স্থগিত করার অনুমতি দেওয়া হয় যতক্ষণ না কোনও কল is.rdbuf()->underflow()আসে until সেন্ড্রি অবজেক্টটি ধ্বংস হওয়ার আগে যদি এই ধরণের কোনও কল না ঘটে তবে ফ্লাশের কলটি সম্পূর্ণরূপে মুছে ফেলা হতে পারে।
chqrlie

সি ++ এর মতো যথারীতি জিনিসগুলি দেখতে দেখতে আরও জটিল। ওপি'র সি ++ গ্রন্থাগারটি স্ট্যান্ডার্ডটি যেভাবে সক্ষম হতে পারে তেমন দক্ষ নয়।
chqrlie

Cppreferences লিঙ্কের জন্য ধন্যবাদ। প্রিন্ট স্ক্রিনে আমি "ভুল উত্তর" পছন্দ করি না যদিও ☺
অ্যালেক্স লোপ।

@ অ্যালেক্সলপ ওহো, "ভুল উত্তর" ইস্যু =) ঠিক করে। অন্য সিনটি আপডেট করতে ভুলে গেছেন (এটি যদিও সময়কে প্রভাবিত করে না)।
ভিটায়ট

@ chqrlie রাইট, তবে স্ট্রডিও সমাধানের তুলনায় আন্ডারফ্লো ক্ষেত্রেও পারফরম্যান্স খারাপ হতে পারে। মানক রেফার জন্য ধন্যবাদ।
ভিটায়ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.