আই ++ এবং ++ আমি সি ++ এর মধ্যে পারফরম্যান্সের পার্থক্য রয়েছে কি?


352

এই দুটি ট্যাগই এই প্রকৃতির প্রশ্নগুলির সন্ধান করার সবচেয়ে সহজ উপায় হওয়ায় আমি ফিরে এসেছি। আমি অন্যদের মধ্যেও গিয়েছিলাম যাদের একত্রীকরণের ট্যাগ নেই এবং তাদেরকে সম্মিলিত ট্যাগ দিয়েছিলাম।
জর্জ স্টিকার

104
সি ++ এবং ++ সি ব্যবহারের মধ্যে পারফরম্যান্সের পার্থক্য রয়েছে কি?
new123456

2
নিবন্ধ: প্রিফিক্স ইনক্রিমেন্ট অপারেটরটি ++ পোস্টফিক্স অপারেটরের পরিবর্তে এটি ++ পুনরুক্তির জন্য ব্যবহার করা কি যুক্তিসঙ্গত? - viva64.com/en/b/0093

উত্তর:


426

[নির্বাহী সংক্ষিপ্তসার: আপনার ব্যবহারের ++iনির্দিষ্ট কারণ না থাকলে ব্যবহার করুন i++]]

সি ++ এর জন্য উত্তরটি কিছুটা জটিল।

যদি iএকটি সাধারণ প্রকার হয় (কোনও সি ++ শ্রেণির উদাহরণ নয়), তবে সি এর জন্য দেওয়া উত্তর ("না কোনও পারফরম্যান্সের পার্থক্য নেই") ধারণ করে, যেহেতু সংকলক কোডটি তৈরি করছে।

যাইহোক, যদি iকোনও সি ++ শ্রেণির উদাহরণ হয়, i++এবং তখন ++iকোনও একটি operator++ফাংশনে কল করছে । এই ফাংশনগুলির জন্য এখানে একটি স্ট্যান্ডার্ড জোড় রয়েছে:

Foo& Foo::operator++()   // called for ++i
{
    this->data += 1;
    return *this;
}

Foo Foo::operator++(int ignored_dummy_value)   // called for i++
{
    Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
    ++(*this);
    return tmp;
}

যেহেতু সংকলক কোডটি জেনারেট করে না, তবে কেবল একটি operator++ফাংশনকে কল করছে, tmpভেরিয়েবল এবং এর সাথে সম্পর্কিত অনুলিপি নির্মাণকারীকে অপ্টিমাইজ করার কোনও উপায় নেই । যদি অনুলিপি নির্মাণকারী ব্যয়বহুল হয়, তবে এটির একটি উল্লেখযোগ্য পারফরম্যান্স প্রভাব থাকতে পারে।


3
সংকলক যা এড়াতে পারে তা হ'ল এনআরভিওর মাধ্যমে কলারের মধ্যে tmp বরাদ্দের মাধ্যমে টিএমপি ফেরত দেওয়ার দ্বিতীয় কপি।
ব্লেজারব্লেড

7
অপারেটর ++ ইনলাইন করা থাকলে সংকলক পুরোপুরি এড়াতে পারবেন না?
এডুয়ার্ড - গ্যাব্রিয়েল মুন্তেণু

16
হ্যাঁ যদি অপারেটর ++ ইনলাইনড থাকে এবং tmp কখনও ব্যবহার না করা হয় তবে tmp অবজেক্টের কনস্ট্রাক্টর বা ডেস্ট্রাক্টরের পার্শ্ব প্রতিক্রিয়া না থাকলে এটিকে সরিয়ে ফেলা যায়।
জ্যান লিংস

5
@ ক্রিস: সি এবং সি ++ এর মধ্যে পার্থক্য হ'ল সিতে আপনার গ্যারান্টি রয়েছে যে অপারেটরটি ইনলাইনড হবে এবং সেই সময়ে একটি শালীন অপ্টিমাইজার পার্থক্যটি সরাতে সক্ষম হবে; পরিবর্তে সি ++ এ আপনি ইনলাইনিং ধরে নিতে পারবেন না - সবসময় নয়।
ব্লেজারব্লেড

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

64

হ্যাঁ. এখানে.

++ অপারেটর একটি ফাংশন হিসাবে সংজ্ঞায়িত বা নাও হতে পারে। আদিম ধরণের জন্য (int, ডাবল, ...) অপারেটরগুলি অন্তর্নির্মিত, তাই সংকলক সম্ভবত আপনার কোডটি অনুকূলিত করতে সক্ষম হবেন। তবে এমন কোনও অবজেক্টের ক্ষেত্রে যেটি ++ অপারেটরকে সংজ্ঞায়িত করে তা আলাদা।

অপারেটর ++ (int) ফাংশন অবশ্যই একটি অনুলিপি তৈরি করতে হবে। এটি কারণ পোস্টফিক্স ++ এটি ধারণ করে তার চেয়ে আলাদা মান ফেরত প্রত্যাশিত: এটি অবশ্যই তার মানটি একটি অস্থায়ী ভেরিয়েবলের মধ্যে ধারণ করে, এর মান বাড়িয়ে দেয় এবং টেম্পটি ফিরিয়ে দেয়। অপারেটর ++ (), উপসর্গ ++ এর ক্ষেত্রে কোনও অনুলিপি তৈরি করার দরকার নেই: অবজেক্টটি নিজে বৃদ্ধি করতে পারে এবং তারপরে কেবল নিজেই ফিরে আসতে পারে।

এখানে পয়েন্টটির একটি উদাহরণ দেওয়া হল:

struct C
{
    C& operator++();      // prefix
    C  operator++(int);   // postfix

private:

    int i_;
};

C& C::operator++()
{
    ++i_;
    return *this;   // self, no copy created
}

C C::operator++(int ignored_dummy_value)
{
    C t(*this);
    ++(*this);
    return t;   // return a copy
}

প্রতিবার যখন আপনি অপারেটরকে ++ (int) কল করবেন তখন আপনাকে অবশ্যই একটি অনুলিপি তৈরি করতে হবে এবং সংকলক এটি সম্পর্কে কিছুই করতে পারে না। পছন্দটি দেওয়া হলে, অপারেটর ++ () ব্যবহার করুন; এইভাবে আপনি একটি অনুলিপি সংরক্ষণ করবেন না। এটি অনেকগুলি ইনক্রিমেন্টের (বৃহত লুপ?) এবং / বা বড় অবজেক্টের ক্ষেত্রে তাৎপর্যপূর্ণ হতে পারে।


2
"প্রাক ইনক্রিমেন্ট অপারেটর কোডে একটি ডেটা নির্ভরতা প্রবর্তন করে: সিপিইউটির বর্ধিত ক্রিয়াকলাপটি পূর্ণ হওয়ার জন্য অপেক্ষা করতে হবে তার অভিব্যক্তিটির মানটি ব্যবহার করার আগে। একটি গভীর পাইপযুক্ত সিপিইউতে, এটি একটি স্টল প্রবর্তন করে no কোনও ডেটা নির্ভরতা নেই is পোস্ট ইনক্রিমেন্ট অপারেটরের জন্য। " ( গেম ইঞ্জিন আর্কিটেকচার (২ য় সংস্করণ) ) সুতরাং কোনও পোস্ট ইনক্রিমেন্টের অনুলিপি যদি কম্পিউটেশনালি নিবিড় না হয় তবে এটি প্রি ইনক্রিমেন্টকে পরাস্ত করতে পারে।
ম্যাথিয়াস

পোস্টফিক্স কোডে, এটি কীভাবে কাজ C t(*this); ++(*this); return t;করবে দ্বিতীয় লাইনে আপনি এই পয়েন্টারটি ডান বাড়িয়ে দিচ্ছেন, তাই আপনি কীভাবে tএটি বাড়িয়ে দিচ্ছেন তা কীভাবে আপডেট হবে। এর মানগুলি কি ইতিমধ্যে অনুলিপি করা হয়নি t?
rasen58

The operator++(int) function must create a copy.না এটা না. এর চেয়ে বেশি অনুলিপিগুলি নেইoperator++()
সেভেরিন পাপ্পাডাক্স

47

যখন ইনক্রিমেন্ট অপারেটরগুলি বিভিন্ন অনুবাদ ইউনিটে থাকে তখন মামলার একটি মানদণ্ড এখানে। জি ++ 4.5 সহ সংকলক।

আপাতত স্টাইলের বিষয়গুলি উপেক্ষা করুন

// a.cc
#include <ctime>
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};

int main () {
    Something s;

    for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
    std::clock_t a = clock();
    for (int i=0; i<1024*1024*30; ++i) ++s;
    a = clock() - a;

    for (int i=0; i<1024*1024*30; ++i) s++; // warm up
    std::clock_t b = clock();
    for (int i=0; i<1024*1024*30; ++i) s++;
    b = clock() - b;

    std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
              << ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
    return 0;
}

ও (এন) ইনক্রিমেন্ট

পরীক্ষা

// b.cc
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};


Something& Something::operator++()
{
    for (auto it=data.begin(), end=data.end(); it!=end; ++it)
        ++*it;
    return *this;
}

Something Something::operator++(int)
{
    Something ret = *this;
    ++*this;
    return ret;
}

ফলাফল

ভার্চুয়াল মেশিনে g ++ 4.5 সহ ফলাফল (সময় সেকেন্ডে হয়):

Flags (--std=c++0x)       ++i   i++
-DPACKET_SIZE=50 -O1      1.70  2.39
-DPACKET_SIZE=50 -O3      0.59  1.00
-DPACKET_SIZE=500 -O1    10.51 13.28
-DPACKET_SIZE=500 -O3     4.28  6.82

ও (1) ইনক্রিমেন্ট

পরীক্ষা

আসুন এখন নীচের ফাইলটি নিই:

// c.cc
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};


Something& Something::operator++()
{
    return *this;
}

Something Something::operator++(int)
{
    Something ret = *this;
    ++*this;
    return ret;
}

এটি বর্ধিতকরণে কিছুই করে না। যখন বর্ধনের ধ্রুবক জটিলতা থাকে তখন এটি কেসটি অনুকরণ করে।

ফলাফল

ফলাফলগুলি এখন অত্যন্ত পরিবর্তিত হয়:

Flags (--std=c++0x)       ++i   i++
-DPACKET_SIZE=50 -O1      0.05   0.74
-DPACKET_SIZE=50 -O3      0.08   0.97
-DPACKET_SIZE=500 -O1     0.05   2.79
-DPACKET_SIZE=500 -O3     0.08   2.18
-DPACKET_SIZE=5000 -O3    0.07  21.90

উপসংহার

পারফরমেন্স ভিত্তিক

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

শব্দার্থিক ভিত্তিক

  • i++বলে increment i, I am interested in the previous value, though
  • ++iবলে increment i, I am interested in the current valueবা increment i, no interest in the previous value। আবার, আপনি এখনই না থাকলেও আপনি অভ্যস্ত হয়ে যাবেন।

Knuth।

অকালীন অপটিমাইজেশন হ'ল সমস্ত অশুভের মূল। যেমন অকাল হতাশা।


1
আকর্ষণীয় পরীক্ষা। এখন, প্রায় আড়াই বছর পরে, gcc 4.9 এবং Clang 3.4 একই ধরণের প্রবণতা দেখায়। উভয়ের সাথে ঝনঝন কিছুটা দ্রুত, তবে প্রাক এবং পোস্টফিক্সের মধ্যে বৈষম্য জিসিসির চেয়ে খারাপ is
মোজা চিবান

আমি যা দেখতে চাই তা হ'ল একটি বাস্তব-বিশ্বের উদাহরণ যেখানে ++ i / i ++ পার্থক্য করে। উদাহরণস্বরূপ, এটি কি কোনও এসটিডি পুনরায় পাঠকের উপর পৃথক করে?
জাকব শোউ জেনসেন

@ জ্যাকব শ্যু জেনসেন: এগুলি বাস্তব বিশ্বের উদাহরণ হওয়ার জন্য সুন্দর উদ্দেশ্য ছিল। জটিল ট্রি স্ট্রাকচার (উদাহরণস্বরূপ কেডি-ট্রি, কোয়াড-ট্রি) বা এক্সপ্রেশন টেম্পলেটগুলিতে ব্যবহৃত বৃহত পাত্রে (সিমডি হার্ডওয়্যারে ডেটা থ্রুটপুট সর্বাধিক করার জন্য) সহ একটি বৃহত অ্যাপ্লিকেশনটি বিবেচনা করুন। যদি সেখানে কোনও পার্থক্য আসে, তবে সত্যিকার অর্থে আমি নিশ্চিত নই যে, কেন নির্দিষ্ট শব্দগুলির জন্য অর্থ-বুদ্ধিমানের প্রয়োজন হয় না, তবে কেন কোনও বিশেষ ক্ষেত্রে পোস্ট-ইনক্রিমেন্টে পড়ে যায়।
সেবাস্তিয়ান মাচ

@ ফ্রেসনেল: আমি মনে করি না যে অপারেটরটি আপনার প্রতিদিনের একটি অভিব্যক্তি টেম্পলেটে থাকে - আপনার কি এর প্রকৃত উদাহরণ আছে? অপারেটর ++ এর সাধারণ ব্যবহারটি পূর্ণসংখ্যা এবং পুনরাবৃত্তকারীগুলিতে। আমি মনে করি যে কোনও পার্থক্য আছে কিনা তা জানতে আগ্রহী হবে (অবশ্যই পূর্ণসংখ্যার সাথে কোনও পার্থক্য নেই - তবে পুনরাবৃত্তকারী)।
জাকব শোউ জেনসেন

@ জ্যাকবসচউ জেনসেন: ব্যবসায়ের প্রকৃত উদাহরণ নেই তবে কিছু সংখ্যার ক্রাঞ্চিং অ্যাপ্লিকেশন রয়েছে যেখানে আপনি স্টাফ গণনা করেন। Wrt পুনরুক্তিকারী, একটি রশ্মির ট্রেসার বিবেচনা করুন যা আইডোমেটিক সি ++ স্টাইলে লেখা হয়েছে এবং আপনার গভীরতা-প্রথম ট্র্যাভারসালগুলির জন্য একটি পুনরুক্তি রয়েছে, যেমন for (it=nearest(ray.origin); it!=end(); ++it) { if (auto i = intersect(ray, *it)) return i; }, প্রকৃত বৃক্ষ কাঠামোর (বিএসপি, কেডি, কোয়াডট্রি, অক্ট্রি গ্রিড ইত্যাদি) কখনও মনে করবেন না। এ ধরনের পুনরুক্তিকারীর কিছু রাষ্ট্র, যেমন বজায় রাখার জন্য প্রয়োজন হবে parent node, child node, indexএবং যে ভালো জিনিস। সব মিলিয়ে আমার অবস্থানটি, এমনকি যদি কয়েকটি মাত্র উদাহরণ উপস্থিত থাকে, ...
সেবাস্তিয়ান মাচ

20

এটি বলা সম্পূর্ণভাবে সঠিক নয় যে সংকলক পোস্টফিক্স ক্ষেত্রে অস্থায়ী পরিবর্তনীয় অনুলিপিটিকে অপ্টিমাইজ করতে পারে না। ভিসির সাথে একটি দ্রুত পরীক্ষা দেখায় যে এটি কমপক্ষে কিছু ক্ষেত্রে এটি করতে পারে।

নিম্নলিখিত উদাহরণে, উত্পন্ন কোডটি উপসর্গ এবং পোস্টফিক্সের জন্য অভিন্ন, উদাহরণস্বরূপ:

#include <stdio.h>

class Foo
{
public:

    Foo() { myData=0; }
    Foo(const Foo &rhs) { myData=rhs.myData; }

    const Foo& operator++()
    {
        this->myData++;
        return *this;
    }

    const Foo operator++(int)
    {
        Foo tmp(*this);
        this->myData++;
        return tmp;
    }

    int GetData() { return myData; }

private:

    int myData;
};

int main(int argc, char* argv[])
{
    Foo testFoo;

    int count;
    printf("Enter loop count: ");
    scanf("%d", &count);

    for(int i=0; i<count; i++)
    {
        testFoo++;
    }

    printf("Value: %d\n", testFoo.GetData());
}

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

for(int i=0; i<10; i++)
{
    testFoo++;
}

printf("Value: %d\n", testFoo.GetData());

নিম্নলিখিত প্রতিক্রিয়া:

00401000  push        0Ah  
00401002  push        offset string "Value: %d\n" (402104h) 
00401007  call        dword ptr [__imp__printf (4020A0h)] 

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


8
আপনি এখানে গুরুত্বপূর্ণ বিষয়টি নোট করতে ভুলে গেছেন। যদি অপারেটরগুলির সংজ্ঞা উপলব্ধ না হয় তবে লাইন বহিরাগত কোডে করা অনুলিপি এড়ানো যায় না; ইনলাইনিংয়ের সাথে সেরাটি সুস্পষ্ট, সুতরাং যে কোনও সংকলক এটি করবে।
ব্লেসরব্ল্যাড

14

গুগল সি ++ স্টাইল গাইড বলেছেন:

প্রিঙ্ক্রেমেন্ট এবং প্রিডিক্রেমেন্ট

পুনরাবৃত্তকারী এবং অন্যান্য টেম্পলেট অবজেক্ট সহ বর্ধিতকরণ এবং হ্রাস অপারেটরগুলির উপসর্গ ফর্ম (++ i) ব্যবহার করুন।

সংজ্ঞা: যখন কোনও ভেরিয়েবল বর্ধিত হয় (++ i বা i ++) বা হ্রাস (- ii বা i--) এবং অভিব্যক্তির মান ব্যবহার করা হয় না, তখন অবশ্যই সিদ্ধান্ত নিতে হবে (প্রবণতা) বা উত্তর-সংযোগ (হ্রাস)।

পেশাদাররা: যখন রিটার্ন মানটিকে অগ্রাহ্য করা হয়, তখন "প্রাক" ফর্ম (++ i) কখনই "পোস্ট" ফর্ম (i ++) এর চেয়ে কম দক্ষ হয় না এবং প্রায়শই আরও দক্ষ হয়। এটি কারণ পোস্ট-ইনক্রিমেন্ট (বা হ্রাস) এর জন্য i এর একটি অনুলিপি প্রয়োজন যা অভিব্যক্তির মান। আমি যদি একজন পুনরুক্তিকারী বা অন্যান্য অ-স্কেলার প্রকার হয় তবে আমার অনুলিপি ব্যয়বহুল হতে পারে। যেহেতু মূল্য উপেক্ষা করা হয় যখন দুটি ধরণের ইনক্রিমেন্ট একই রকম আচরণ করে, তবে কেন সবসময় প্রাক-বৃদ্ধি বৃদ্ধি করা হয় না?

কনস: অভিব্যক্তির মানটি বিশেষত লুপের ক্ষেত্রে ব্যবহার না করা হলে পোস্ট-ইনক্রিমেন্ট ব্যবহারের Cতিহ্য সি তে উন্নত হয়েছিল। "সাবজেক্ট" (i) ইংরেজির মতো "ক্রিয়া" (++) এর আগে যেহেতু কেউ কেউ পোস্ট-ইনক্রিমেন্ট পড়া সহজ মনে করেন।

সিদ্ধান্ত: সাধারণ স্কেলারের (অ-অবজেক্ট) মানগুলির জন্য একটি ফর্মকে প্রাধান্য দেওয়ার কোনও কারণ নেই এবং আমরা উভয়কেই অনুমতি দিই। পুনরাবৃত্তকারী এবং অন্যান্য টেম্পলেট ধরণের জন্য, প্রাক-বর্ধন ব্যবহার করুন।


1
"সিদ্ধান্ত: সাধারণ স্কেলারের (অ-অবজেক্ট) মানগুলির জন্য একটি ফর্মকে প্রাধান্য দেওয়ার কোনও কারণ নেই এবং আমরা উভয়কেই অনুমতি দিই ite পুনরাবৃত্তকারী এবং অন্যান্য টেম্পলেট ধরণের জন্য, প্রাক-বর্ধন ব্যবহার করুন" "
নসরেডনা

2
এহ, ..., এবং এটি কী?
সেবাস্তিয়ান মাচ

উত্তরের উল্লিখিত লিঙ্কটি বর্তমানে ভেঙে গেছে
করোল

4

আমি খুব সম্প্রতি কোড টক-এ অ্যান্ড্রু কোইনিগের একটি দুর্দান্ত পোস্টটি উল্লেখ করতে চাই।

http://dobbscodetalk.com/index.php?option=com_myblog&show=Efficiency-versus-intent.html&Itemid=29

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

সুতরাং, প্রথমে আপনার অভিপ্রায় স্থির করুন এবং প্রাক বা পোস্ট যদি গুরুত্বপূর্ণ না হয় তবে প্রাকের সাথে যান কারণ এতে অতিরিক্ত বস্তুর সৃষ্টি এড়ানো এবং নিক্ষেপ করে কিছুটা পারফরম্যান্স উপকার পাবেন।



4

@Ketan

... অভিপ্রায় বনাম পারফরম্যান্সের বিষয়ে ওভার-লুক করা বিশদ উত্থাপন করে। এমন সময় আছে যখন আমরা ++ ইটারের পরিবর্তে ++ ব্যবহার করতে চাই।

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

সর্বোপরি যে কারণে ভাষাটি বলা হয় না "++C " । [*]

[*] ++Cআরও যুক্তিযুক্ত নাম হওয়া সম্পর্কে বাধ্যতামূলক আলোচনা sertোকান ।


4
@ মট্টি: (কৌতুক করে) সি ++ নামটি যৌক্তিক যদি আপনি বার্জন স্ট্রোস্ট্রাপ সি ++ কে প্রাথমিকভাবে একটি সি প্রোগ্রাম তৈরির প্রাক-সংকলক হিসাবে কোড করে মনে করেন। সুতরাং সি ++ একটি পুরানো সি মান ফেরত দিয়েছে। বা এটি বর্ধিত করা হতে পারে যে সি ++ শুরু থেকেই কিছুটা ধারণাগতভাবে ত্রুটিযুক্ত।
310

4
  1. ++ i - দ্রুততর মানটি ব্যবহার না করে
  2. আমি ++ - দ্রুত ফেরতের মান ব্যবহার করে

যখন ব্যবহার করছেন না ফেরত মান কম্পাইলার ক্ষেত্রে একটি অস্থায়ী ব্যবহার না করার নিশ্চিত করা হয় আমি ++, । দ্রুত হওয়ার গ্যারান্টি নেই তবে ধীর না হওয়ার গ্যারান্টি রয়েছে।

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


3

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

এটি বলেছে, বেশিরভাগ ক্ষেত্রে ++ ইটার ব্যবহার করা আরও ভাল স্টাইল। :-)


3

মধ্যে কর্মক্ষমতা পার্থক্য ++iএবং i++যখন আপনি যেমন মূল্য ফেরার ফাংশন অপারেটরদের মনে করি এবং কিভাবে তারা প্রয়োগ করা হয় আরো আপাত হবে। এটিকে সহজভাবে বোঝার জন্য কি ঘটছে করতে, নিম্নলিখিত কোড উদাহরণ ব্যবহার করবে intযেমন যদি এটি একটি ছিল struct

++iপরিবর্তনশীল বৃদ্ধি করে, তারপরে ফলাফলটি দেয়। এটি যথাস্থানে এবং ন্যূনতম সিপিইউ সময় সহ করা যেতে পারে, অনেক ক্ষেত্রে কেবলমাত্র একটি লাইন কোডের প্রয়োজন:

int& int::operator++() { 
     return *this += 1;
}

তবে একই কথা বলা যায় না i++

পোস্ট-ইনক্রিমেন্টিং, i++প্রায়শই বর্ধিত হওয়ার আগে আসল মান ফেরত দেখা যায় । যাইহোক, একটি ফাংশন শুধুমাত্র ফলে যখন এটি সমাপ্ত হয় আসতে পারেন । ফলস্বরূপ, আসল মান সম্বলিত ভেরিয়েবলের একটি অনুলিপি তৈরি করা, ভেরিয়েবলকে বৃদ্ধি করা, তারপরে মূল মানটি ধারণ করে অনুলিপিটি প্রদান করা প্রয়োজনীয় হয়ে পড়ে:

int int::operator++(int& _Val) {
    int _Original = _Val;
    _Val += 1;
    return _Original;
}

প্রাক-বৃদ্ধি এবং উত্তর-বৃদ্ধির মধ্যে যখন কোনও কার্যকরী পার্থক্য না থাকে, তখন সংকলকটি অপ্টিমাইজেশন সম্পাদন করতে পারে যে উভয়ের মধ্যে কোনও পারফরম্যান্সের পার্থক্য নেই। তবে, যদি কোনও যৌগিক ডেটা টাইপ যেমন একটি structবা classজড়িত থাকে তবে অনুলিপি পোস্ট-ইনক্রিমেন্টে অনুলিপি করা হবে এবং যদি একটি গভীর অনুলিপি প্রয়োজন হয় তবে এই অপ্টিমাইজেশন সম্পাদন করা সম্ভব হবে না। যেমন, প্রাক-বৃদ্ধি সাধারণত দ্রুত হয় এবং পোস্ট-ইনক্রিমেন্টের চেয়ে কম মেমরির প্রয়োজন হয়।


1

@ মার্ক: আমি আমার পূর্ববর্তী উত্তরটি মুছলাম কারণ এটি কিছুটা উল্টানো ছিল এবং কেবলমাত্র এটির জন্য একটি ডাউনটোটের যোগ্য ছিল। আমি আসলে এটি একটি ভাল প্রশ্ন এই অর্থে যে এটি জিজ্ঞাসা করে যে অনেক মানুষের মনে কি আছে।

সাধারণ উত্তরটি হ'ল ++ i আমি ++ এর চেয়ে দ্রুত এবং এটি সন্দেহ নেই তবে বড় প্রশ্নটি "আপনার কখন যত্ন করা উচিত?"

পুনরবৃদ্ধি পুনরুক্তিতে ব্যয় করা সিপিইউ সময়ের ভগ্নাংশ যদি 10% এর চেয়ে কম হয়, তবে আপনি হয়ত যত্ন করবেন না।

যদি বার্ষিকী পুনরবৃদ্ধি করতে ব্যয় করা সিপিইউ সময়ের ভগ্নাংশটি 10% এর চেয়ে বেশি হয়, আপনি কোন স্টেটমেন্টগুলি পুনরাবৃত্তি করছে তা আপনি দেখতে পারেন। আপনি পুনরাবৃত্তকারী ব্যবহার না করে কেবল পূর্ণসংখ্যার বৃদ্ধি করতে পারেন কিনা তা দেখুন। সম্ভাবনাগুলি আপনি যা করতে পেরেছিলেন এবং এটি কিছুটা বিবেচনায় কম কাঙ্ক্ষিত হলেও সম্ভাবনাগুলি বেশ ভাল আপনি সেই পুনরাবৃত্তকারীগুলিতে ব্যয় করা সমস্ত সময় সাশ্রয় করবেন।

আমি এমন একটি উদাহরণ দেখেছি যেখানে 90% সময়ের মধ্যে পুনরাবৃত্তির পরিমাণ বাড়ানো ছিল। সেক্ষেত্রে, পূর্ণসংখ্যার-বর্ধিতকরণে মূলত সেই পরিমাণ দ্বারা কার্যকরকরণের সময় হ্রাস। (যেমন 10x স্পিডআপের চেয়ে ভাল)


1

@wilhelmtell

সংকলক অস্থায়ীভাবে elide করতে পারেন। অন্য থ্রেড থেকে ভারব্যাটিম:

সি ++ সংকলকটিকে স্ট্যাক ভিত্তিক টেম্পোরারিগুলি মুছে ফেলার অনুমতি দেওয়া হয় এমনকি যদি এটি করে তবে এর আচরণগত আচরণ পরিবর্তন হয়। ভিসি 8 এর জন্য এমএসডিএন লিঙ্ক:

http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx


1
এটি প্রাসঙ্গিক নয়। এনআরভিও "সিসি :: অপারেটর ++ (ইনট)" টি কলারে ফিরে অনুলিপি করার প্রয়োজন এড়ায়, তবে আমি ++ কলারের স্ট্যাকের পুরানো মানটি অনুলিপি করব। এনআরভিও ব্যতীত, আমি ++ 2 টি অনুলিপি তৈরি করি, এক থেকে টি এবং এক কলারে ফিরে।
ব্লেজারব্লেড

0

আপনি যে বিল্ট-ইন প্রকারের যেখানে কোনও পারফরম্যান্স সুবিধা নেই সেখানে নিজের জন্য একটি ভাল অভ্যাস তৈরি করা কেন ++ i ব্যবহার করা উচিত তার কারণ।


3
দুঃখিত, কিন্তু এটি আমাকে বিরক্ত করে। কে বলে যে এটি একটি "ভাল অভ্যাস", যখন এটি প্রায় কখনই গুরুত্ব দেয় না? লোকেরা যদি এটিকে তাদের শৃঙ্খলার অংশ করতে চায় তবে তা ঠিক আছে তবে আসুন ব্যক্তিগত রুচির বিষয়গুলির থেকে উল্লেখযোগ্য কারণগুলি আলাদা করতে পারি।
মাইক ডুনলাভে

@ মাইকডুনলাভে ঠিক আছে, তাই যখন আপনি কোন ব্যাপার না তখন সাধারণত কোন দিকটি ব্যবহার করেন? এক্সডি এটি হয় এক না অন্যটি নয়! পোস্ট ++ (আপনি যদি এটি সাধারণ অর্থ দিয়ে ব্যবহার করছেন it তবে এটি আপডেট করুন, পুরানোটি ফিরিয়ে দিন) সম্পূর্ণরূপে নিকৃষ্ট হয় ++ প্রাক (এটি আপডেট করুন, ফিরে আসুন) এরকম কোনও কারণ নেই যা আপনার কম পারফরম্যান্স করতে চান never আপনি যেখানে এটি আপডেট করতে চান সেই ক্ষেত্রে, প্রোগ্রামার এমনকি পোস্টটি ++ এগুলি এখনও করবে না। আমাদের কাছে ইতিমধ্যে এটি অনুলিপি করার সময় নষ্ট করা উচিত নয়। এটি ব্যবহারের পরে এটি আপডেট করুন। তারপরে সাধারণ সংবেদনযুক্ত সংকলকগুলির কাছে এটি আপনি চেয়েছিলেন।
পুডল

@ পুডল: আমি যখন শুনি: "আপনার কম পারফরম্যান্স করতে চান এমন কোনও কারণ নেই" আমি জানি আমি "পেনি বুদ্ধিমান - পাউন্ড বোকা" শুনছি। জড়িত প্রশস্ততাগুলির আপনার একটি উপলব্ধি হওয়া দরকার। যদি এই অ্যাকাউন্টে জড়িত 1% এরও বেশি সময় ধরে জড়িত তবে আপনি এটিরও ভাবনা জানান। সাধারণত, আপনি যদি এটি নিয়ে ভাবছেন তবে মিলিয়ন-গুণ বৃহত্তর সমস্যাগুলি আপনি বিবেচনা করছেন না এবং এটিই সফ্টওয়্যারটির তুলনায় অনেক ধীর গতির হয়ে যায়।
মাইক ডুনলাভে

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

0

উভয়ই তত দ্রুত;) আপনি যদি এটি চান যে এটি প্রসেসরের জন্য একই গণনা, এটি কেবল এটির ক্রম হিসাবে এটি করা হয় যা ভিন্ন।

উদাহরণস্বরূপ, নিম্নলিখিত কোড:

#include <stdio.h>

int main()
{
    int a = 0;
    a++;
    int b = 0;
    ++b;
    return 0;
}

নিম্নলিখিত সমাবেশ উত্পাদন:

 0x0000000100000f24 <main+0>: push   %rbp
 0x0000000100000f25 <main+1>: mov    %rsp,%rbp
 0x0000000100000f28 <main+4>: movl   $0x0,-0x4(%rbp)
 0x0000000100000f2f <main+11>:    incl   -0x4(%rbp)
 0x0000000100000f32 <main+14>:    movl   $0x0,-0x8(%rbp)
 0x0000000100000f39 <main+21>:    incl   -0x8(%rbp)
 0x0000000100000f3c <main+24>:    mov    $0x0,%eax
 0x0000000100000f41 <main+29>:    leaveq 
 0x0000000100000f42 <main+30>:    retq

আপনি দেখতে পাচ্ছেন যে একটি ++ এবং বি ++ এর জন্য এটি একটি স্মৃতিসৌধিক, তাই এটি একই ক্রিয়াকলাপ;)


এটি সি, যখন ওপি সি ++ জিজ্ঞাসা করেছিল। সি তে এটি একই রকম। সি ++ এ দ্রুততর ++ i; কারণ তার উদ্দেশ্য। তবে কিছু সংকলক পোস্ট-ইনক্রিমেন্ট অপারেটরটিকে অনুকূল করতে পারে।
উইগলগার জেটাগ

0

ফলাফলটি কখন অব্যবহৃত হবে তা উদ্দেশ্যযুক্ত প্রশ্ন ছিল (এটি সি এর প্রশ্ন থেকে পরিষ্কার)। প্রশ্নটি "সম্প্রদায় উইকি" হওয়ায় কেউ কি এটিকে সমাধান করতে পারবেন?

অকাল অপ্টিমাইজেশান সম্পর্কে, নথ প্রায়শই উদ্ধৃত হয়। সেটা ঠিক. তবে ডোনাল্ড নুথ কখনই সেই ভয়াবহ কোডটি ব্যবহার করতে পারবেন না যা আপনি এই দিনগুলিতে দেখতে পাচ্ছেন। জাভা ইন্টিজারগুলির মধ্যে কখনই a = b + c দেখেছেন? যার পরিমাণ 3 বক্সিং / আনবক্সিং রূপান্তর। এ জাতীয় জিনিস এড়ানো গুরুত্বপূর্ণ। এবং অকেজোভাবে ++ i এর পরিবর্তে i ++ লেখা একই ভুল। সম্পাদনা: ফ্রেসনেল এটিকে একটি মন্তব্যে খুব সুন্দর করে বলেছে, এটি "অকালীন নিরাশা যেমন অকাল অপটিমাইজেশনটি মন্দ," হিসাবে সংক্ষেপে বলা যেতে পারে।

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

বিটিডব্লিউ, আমি এমন একজন যারা আমি ++ পড়ার জন্য বিরক্তিকর সন্ধান করতে সি ++ তে যথেষ্ট ব্যবহার করি নি। তবুও, আমি যেহেতু আমি স্বীকার করি যে এটি সঠিক use


আমি দেখছি আপনি পিএইচডি করার জন্য কাজ করছেন সংকলক অপ্টিমাইজেশান এবং এই ধরণের জিনিস আগ্রহী। এটি দুর্দান্ত, তবে ভুলে যাবেন না একাডেমীয়া একটি প্রতিধ্বনির চেম্বার এবং সাধারণ জ্ঞান প্রায়শই দরজার বাইরে চলে যায়, কমপক্ষে সিএসে আপনি এতে আগ্রহী হতে পারেন: stackoverflow.com/questions/1303899/...
মাইক Dunlavey

এর ++iচেয়ে বেশি বিরক্তিকর আমি আর পাই নাi++ (বাস্তবে আমি এটি আরও শীতল পেয়েছি), তবে আপনার পোস্টের বাকী অংশটি আমার সম্পূর্ণ স্বীকৃতি পেয়েছে। হতে পারে একটি বিন্দু যুক্ত করুন "অকাল অনুকূলতা যেমন খারাপ, তেমনি অকাল হতাশা"
সেবাস্তিয়ান

strncpyতারা সেই সময় যে ফাইল ফাইলগুলি ব্যবহার করত সেগুলিতে একটি উদ্দেশ্য সরবরাহ করেছিল; ফাইলের নামটি একটি 8-চরিত্রের বাফার এবং এটি নাল-টার্মিনেট করতে হবে না। ভাষার বিবর্তনের ভবিষ্যতে 40 বছর না দেখার জন্য আপনি তাদের দোষ দিতে পারবেন না।
এমএম

@ ম্যাটম্যাকনাব: ৮ টি অক্ষরের ফাইলের নাম এমএস-ডস একচেটিয়া ছিল না? সি ইউনিক্স দিয়ে উদ্ভাবিত হয়েছিল। যাইহোক, এমনকি যদি strncpy এর একটি বিন্দু থাকে, তবে strlcpy এর অভাব পুরোপুরি ন্যায়সঙ্গত হয়নি: এমনকি আসল সিতে অ্যারেগুলি ছিল যা আপনার ওভারফ্লো হওয়া উচিত নয়, যার জন্য strlcpy প্রয়োজন; বেশিরভাগ ক্ষেত্রে, তারা বাগগুলি শোষণের উদ্দেশ্যে কেবল আক্রমণকারীদের অনুপস্থিত ছিল। তবে কেউ বলতে পারেন না যে এই সমস্যাটির পূর্বাভাস তুচ্ছ ছিল, সুতরাং আমি যদি আমার পোস্টটি আবার লিখি তবে আমি একই স্বরটি ব্যবহার করব না।
ব্লেজারব্লেড

@ ব্লাইজারব্ল্যাড: আমার মনে আছে, ইউনিক্সের প্রথম দিকের ফাইলের নামগুলি 14 টি অক্ষরের মধ্যে সীমাবদ্ধ ছিল। অভাবটি strlcpy()সত্যটি প্রমাণিত হয়েছিল যে এটি এখনও আবিষ্কার হয়নি।
কিথ থমসন

-1

লোকদেরকে জ্ঞানের রত্ন প্রদানের সময়;) - সি ++ পোস্টফিক্স ইনক্রিমেন্টটি উপসর্গের বর্ধনের মতোই একই রকম আচরণ করার সহজ কৌশল আছে (এটি আমার জন্য এটি উদ্ভাবিত হয়েছে, তবে এটি অন্য লোকের কোডেও দেখেছি, তাই আমি নই একা)।

মূলত, কৌশলটি হ'ল প্রত্যাবর্তনের পরে বর্ধিততা স্থগিত করার জন্য সহায়ক শ্রেণী ব্যবহার করা এবং আরএআইআই উদ্ধার করতে আসে

#include <iostream>

class Data {
    private: class DataIncrementer {
        private: Data& _dref;

        public: DataIncrementer(Data& d) : _dref(d) {}

        public: ~DataIncrementer() {
            ++_dref;
        }
    };

    private: int _data;

    public: Data() : _data{0} {}

    public: Data(int d) : _data{d} {}

    public: Data(const Data& d) : _data{ d._data } {}

    public: Data& operator=(const Data& d) {
        _data = d._data;
        return *this;
    }

    public: ~Data() {}

    public: Data& operator++() { // prefix
        ++_data;
        return *this;
    }

    public: Data operator++(int) { // postfix
        DataIncrementer t(*this);
        return *this;
    }

    public: operator int() {
        return _data;
    }
};

int
main() {
    Data d(1);

    std::cout <<   d << '\n';
    std::cout << ++d << '\n';
    std::cout <<   d++ << '\n';
    std::cout << d << '\n';

    return 0;
}

কিছু ভারী কাস্টম পুনরাবৃত্তকারী কোডের জন্য উদ্ভাবিত এবং এটি রান-টাইমকে হ্রাস করে। উপসর্গ বনাম পোস্টফিক্সের দাম এখন একটি রেফারেন্স, এবং এটি যদি কাস্টম অপারেটর যদি ভারী ঘোরাঘুরি করে, উপসর্গ এবং পোস্টফিক্সটি আমার জন্য একই রান-টাইম অর্জন করেছিল।


-5

++i তুলনায় দ্রুত i++এটির কারণ এটি মানটির একটি পুরানো অনুলিপি ফেরত দেয় না।

এটি আরও স্বজ্ঞাত:

x = i++;  // x contains the old value of i
y = ++i;  // y contains the new value of i 

এই সি উদাহরণটি আপনি আশা করতে পারেন "12" এর পরিবর্তে "02" মুদ্রণ করে:

#include <stdio.h>

int main(){
    int a = 0;
    printf("%d", a++);
    printf("%d", ++a);
    return 0;
}

সি ++ এর জন্য একই :

#include <iostream>
using namespace std;

int main(){
    int a = 0;
    cout << a++;
    cout << ++a;
    return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.