স্ট্যান্ড :: ভেক্টর কি সরল অ্যারেগুলির তুলনায় এত ধীর?


212

আমি সবসময় ভেবেছি এটিই সাধারণ জ্ঞান যা std::vector"অ্যারে হিসাবে প্রয়োগ করা হয়" ব্লা ব্লা ব্লাহ। আজ আমি নীচে নেমে এটি পরীক্ষা করে দেখলাম এবং এটি এমনটা মনে হচ্ছে না:

এখানে কিছু পরীক্ষার ফলাফল রয়েছে:

UseArray completed in 2.619 seconds
UseVector completed in 9.284 seconds
UseVectorPushBack completed in 14.669 seconds
The whole thing completed in 26.591 seconds

এটি প্রায় 3 - 4 বার ধীর! " vectorকয়েকটি ন্যানোসেকের জন্য ধীর হতে পারে" মন্তব্যগুলির জন্য সত্যই সমর্থন করে না ।

এবং আমি যে কোডটি ব্যবহার করেছি:

#include <cstdlib>
#include <vector>

#include <iostream>
#include <string>

#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>

class TestTimer
{
    public:
        TestTimer(const std::string & name) : name(name),
            start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
        {
        }

        ~TestTimer()
        {
            using namespace std;
            using namespace boost;

            posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
            posix_time::time_duration d = now - start;

            cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
                " seconds" << endl;
        }

    private:
        std::string name;
        boost::posix_time::ptime start;
};

struct Pixel
{
    Pixel()
    {
    }

    Pixel(unsigned char r, unsigned char g, unsigned char b) : r(r), g(g), b(b)
    {
    }

    unsigned char r, g, b;
};

void UseVector()
{
    TestTimer t("UseVector");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel> pixels;
        pixels.resize(dimension * dimension);

        for(int i = 0; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }
    }
}

void UseVectorPushBack()
{
    TestTimer t("UseVectorPushBack");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel> pixels;
            pixels.reserve(dimension * dimension);

        for(int i = 0; i < dimension * dimension; ++i)
            pixels.push_back(Pixel(255, 0, 0));
    }
}

void UseArray()
{
    TestTimer t("UseArray");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        Pixel * pixels = (Pixel *)malloc(sizeof(Pixel) * dimension * dimension);

        for(int i = 0 ; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }

        free(pixels);
    }
}

int main()
{
    TestTimer t1("The whole thing");

    UseArray();
    UseVector();
    UseVectorPushBack();

    return 0;
}

আমি কি এটা ভুল করছি বা কিছু করছি? নাকি আমি এই পারফরম্যান্সের পৌরাণিক কাহিনী স্রেফ ছড়িয়েছি?

আমি ভিজ্যুয়াল স্টুডিও 2005 এ রিলিজ মোড ব্যবহার করছি ।


ইন ভিসুয়াল সি ++ , #define _SECURE_SCL 0হ্রাস UseVectorঅর্ধেক দ্বারা (এটি আনয়ন নিচে 4 সেকেন্ড)। এটি সত্যিই বিশাল, আইএমও।


23
আপনি ডিবাগ মোডে থাকাকালীন ভেক্টরের কিছু সংস্করণ অতিরিক্ত নির্দেশাবলী যুক্ত করে পরীক্ষা করে নিন যে অ্যারে এবং এর মতো স্টাফের শেষের বাইরে আপনি অ্যাক্সেস করেন না। আসল সময় পেতে আপনাকে অবশ্যই রিলিজ মোডে তৈরি করতে হবে এবং অপ্টিমাইজেশনগুলি চালু করতে হবে।
মার্টিন ইয়র্ক

40
এটি ভাল যে আপনি ইন্টারনেটে শুনেছেন এমন বিশ্বাসের দাবির পরিবর্তে আপনি মাপ করেছেন।
পি

51
ভেক্টর হয় একটি অ্যারের হিসাবে প্রয়োগ করা। এটি "প্রচলিত জ্ঞান" নয়, এটি সত্য। আপনি আবিষ্কার করেছেন যে vectorএটি একটি সাধারণ উদ্দেশ্যে পুনরায় আকার পরিবর্তনযোগ্য অ্যারে। অভিনন্দন। সমস্ত সাধারণ উদ্দেশ্যে সরঞ্জামগুলির মতো এটি বিশেষায়িত পরিস্থিতি সহ সম্ভব যেখানে এটি সাব-অনুকূল op যে কারণে প্রচলিত জ্ঞান হ'ল একটি দিয়ে শুরু করা vectorএবং প্রয়োজনীয় হলে বিকল্পগুলি বিবেচনা করা।
ডেনিস জিকিফুজ

37
হ্যাঁ, "একটি ডুবে নোংরা থালা নিক্ষেপ করা" এবং "নোংরা থালাগুলি একটি ডোবাতে ফেলে দেওয়া এবং তা ভেঙে গেছে কিনা তা পরীক্ষা করার" গতির পার্থক্য কী?
ইম্রে এল

9
VC2010 এ কমপক্ষে এটির প্রধান পার্থক্যটি মনে হচ্ছে যে ম্যালোক () আকার পরিবর্তন () এর চেয়ে দ্রুত। সময় থেকে মেমরি বরাদ্দ সরান, _ITERATOR_DEBUG_LEVEL == 0 দিয়ে সংকলন করুন এবং ফলাফলগুলি একই are
আন্দ্রেয়াস ম্যাগনুসন

উত্তর:


260

নিম্নলিখিত ব্যবহার করে:

ছ ++, -O3 Time.cpp -আমি <MyBoost>
./a.out
UseArray 2,196 সেকেন্ডের মধ্যে সম্পন্ন
4,412 সেকেন্ডের মধ্যে সম্পন্ন UseVector
8,017 সেকেন্ডের মধ্যে সম্পন্ন UseVectorPushBack
পুরো জিনিস 14,626 সেকেন্ডের মধ্যে সম্পন্ন

সুতরাং অ্যারে ভেক্টরের চেয়ে দ্বিগুণ দ্রুত।

তবে কোডটি আরও বিশদে দেখার পরে এটি প্রত্যাশিত; যেমন আপনি দু'বার ভেক্টর জুড়ে এবং অ্যারে একবারে চালাবেন। দ্রষ্টব্য: যখন আপনি resize()ভেক্টর আপনি কেবল মেমরি বরাদ্দ না করে ভেক্টর দিয়ে চলছেন এবং প্রতিটি সদস্যকে কনস্ট্রাক্টরকে কল করছেন।

কোডটি কিছুটা পুনরায় সাজানো যাতে ভেক্টর প্রতিটি বস্তুকে একবার একবার শুরু করে:

 std::vector<Pixel>  pixels(dimensions * dimensions, Pixel(255,0,0));

এখন আবার একই সময় করা:

ছ ++, -O3 Time.cpp -আমি <MyBoost>
./a.out
UseVector 2,216 সেকেন্ডের মধ্যে সম্পন্ন

ভেক্টর এখন আরে থেকে কিছুটা খারাপ পারফরম্যান্স করছে। আইএমও এই পার্থক্য তুচ্ছ এবং পরীক্ষার সাথে জড়িত না এমন গোটা গোছগুলির কারণে হতে পারে।

আমি এটিও ધ્યાનમાં রাখব যে আপনি UseArrray()পদ্ধতিতে পিক্সেল অবজেক্টটি সঠিকভাবে আরম্ভ / ধ্বংস করছেন না কারণ নির্মাণকারী / ধ্বংসকারীকে বলা হয় না (এটি এই সাধারণ শ্রেণীর জন্য সমস্যা নয় তবে কিছুটা জটিল জটিল (যেমন পয়েন্টার বা সদস্য সহ) পয়েন্টার সহ) সমস্যার কারণ হবে।


48
@ kizzx2: এর reserve()পরিবর্তে আপনার প্রয়োজন resize()। এটি বস্তুর জন্য স্থান বরাদ্দ করে ( এটি ভেক্টরের ক্ষমতা পরিবর্তন করে ) তবে বস্তু তৈরি করে না (অর্থাৎ, ভেক্টরের আকার অপরিবর্তিত রয়েছে)।
জেমস ম্যাকনেলিস

25
আপনি 1 000 000 000 অ্যারে অ্যাক্সেসগুলি করছেন। সময়ের পার্থক্য 0.333 সেকেন্ড। বা অ্যারে অ্যাক্সেস প্রতি 0.000000000333 এর পার্থক্য। আমার মতো একটি 2.33 গিগাহার্টজ প্রসেসর ধরে নেওয়া যা অ্যারে অ্যাক্সেসের জন্য 0.7 নির্দেশিকা পাইপলাইন ধাপ। সুতরাং ভেক্টরটিকে দেখে মনে হচ্ছে এটি অ্যাক্সেসের জন্য আরও একটি অতিরিক্ত নির্দেশ ব্যবহার করছে।
মার্টিন ইয়র্ক

3
@ জেমস ম্যাকনেলিস: আপনি কেবল এটির resize()সাথে প্রতিস্থাপন করতে পারবেন না reserve(), কারণ এটি ভেক্টরের নিজস্ব আকারের অভ্যন্তরীণ ধারণাটি সামঞ্জস্য করে না, সুতরাং পরবর্তী উপাদানগুলি সম্পর্কে লেখাগুলি প্রযুক্তিগতভাবে "শেষের দিকে লেখার" এবং ইউবি তৈরি করবে। যদিও বাস্তবে প্রতিটি এসটিএল বাস্তবায়ন সে ক্ষেত্রে "নিজেকে আচরণ করবে", তবে আপনি কীভাবে ভেক্টরের আকারটিকে পুনরায় সংযুক্ত করবেন? যদি আপনি ভেক্টরকে পপুলেশন করার resize() পরে কল করার চেষ্টা করেন তবে এটি সম্ভবত ডিফল্ট-নির্মিত মান সহ সমস্ত উপাদানকে ওভাররাইট করে দেবে!
j_random_hacker

8
@ জেআর্যান্ডম_হ্যাকার: আমি যা বলেছিলাম তা কি তাই না? আমি ভেবেছিলাম আমি খুব স্পষ্ট যে reserveকেবল একটি ভেক্টরের ক্ষমতা পরিবর্তন করে, এর আকার নয়।
জেমস ম্যাকনেলিস

7
ঠিক আছে, ফিগার যান ভেক্টর পদ্ধতিতে প্রচুর ব্যতিক্রম সম্পর্কিত ক্রাফট ছিল। যোগ করার পদ্ধতি /EHscসংকলন যে পরিষ্কার সুইচ এবং assign()আসলে এখন অ্যারের beats। হ্যাঁ.
পাভেল মিনায়েভ

55

দুর্দান্ত প্রশ্ন। আমি এখানে এমন কিছু সাধারণ ফিক্স খুঁজে পাওয়ার প্রত্যাশায় এসেছি যা ভেক্টর পরীক্ষাগুলি ঠিক করে দেবে। আমি যেমন আশা করেছিলাম তেমন কার্যকর হয়নি!

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

# g++ -Wall -Wextra -pedantic -o vector vector.cpp
# ./vector
UseArray completed in 20.68 seconds
UseVector completed in 120.509 seconds
UseVectorPushBack completed in 37.654 seconds
The whole thing completed in 178.845 seconds
# g++ -Wall -Wextra -pedantic -O3 -o vector vector.cpp
# ./vector
UseArray completed in 3.09 seconds
UseVector completed in 6.09 seconds
UseVectorPushBack completed in 9.847 seconds
The whole thing completed in 19.028 seconds

আইডিয়া # 1 - malloc এর পরিবর্তে নতুন [] ব্যবহার করুন

আমি পরিবর্তন চেষ্টা malloc()করতে new[]তাই বস্তু নির্মাণ পেতে হবে UseArray হবে। এবং পৃথক ক্ষেত্রের অ্যাসাইনমেন্ট থেকে পিক্সেল উদাহরণ বরাদ্দ করা হচ্ছে। ওহ, এবং অভ্যন্তরীণ লুপের পরিবর্তনশীলটির নামকরণ j

void UseArray()
{
    TestTimer t("UseArray");

    for(int i = 0; i < 1000; ++i)
    {   
        int dimension = 999;

        // Same speed as malloc().
        Pixel * pixels = new Pixel[dimension * dimension];

        for(int j = 0 ; j < dimension * dimension; ++j)
            pixels[j] = Pixel(255, 0, 0);

        delete[] pixels;
    }
}

আশ্চর্যরূপে (আমার কাছে), এই পরিবর্তনগুলির মধ্যে কোনওটিই কোনও পার্থক্য তৈরি করেনি। এমনকি new[]পিক্সেলগুলির সমস্তই ডিফল্টরূপে নির্মাণ করবে এমন পরিবর্তনও নয় । মনে হচ্ছে যে জিসিসি ব্যবহার করে যখন ডিফল্ট কন্সট্রাকটর কল আউট নিখুত করতে new[], কিন্তু ব্যবহার করছেন না যখন vector

আইডিয়া # 2 - পুনরাবৃত্ত অপারেটর [] কলগুলি সরান

আমি ট্রিপল operator[]লুকআপ থেকে মুক্তি পাওয়ার চেষ্টা করেছি এবং রেফারেন্সটি ক্যাশে করেছি pixels[j]। এটি আসলে ব্যবহারকারীর গতি কমিয়ে দিয়েছে! উফ।

for(int j = 0; j < dimension * dimension; ++j)
{
    // Slower than accessing pixels[j] three times.
    Pixel &pixel = pixels[j];
    pixel.r = 255;
    pixel.g = 0;
    pixel.b = 0;
}

# ./vector 
UseArray completed in 3.226 seconds
UseVector completed in 7.54 seconds
UseVectorPushBack completed in 9.859 seconds
The whole thing completed in 20.626 seconds

আইডিয়া # 3 - কনস্ট্রাক্টরগুলি সরান

সম্পূর্ণরূপে নির্মাণকারীদের অপসারণ সম্পর্কে কী? তারপরে যখন ভেক্টরগুলি তৈরি করা হয় তখন জিসিসি সমস্ত বস্তুর নির্মাণকে অনুকূল করে তুলতে পারে। আমরা যদি পিক্সেল এ পরিবর্তন করি তবে কী হয়:

struct Pixel
{
    unsigned char r, g, b;
};

ফলাফল: প্রায় 10% দ্রুত। একটি অ্যারের চেয়ে এখনও ধীর। হুঁ।

# ./vector 
UseArray completed in 3.239 seconds
UseVector completed in 5.567 seconds

আইডিয়া # 4 - লুপ সূচীর পরিবর্তে পুনরুক্তি ব্যবহার করুন

vector<Pixel>::iteratorলুপ ইনডেক্সের পরিবর্তে কীভাবে ব্যবহার করবেন ?

for (std::vector<Pixel>::iterator j = pixels.begin(); j != pixels.end(); ++j)
{
    j->r = 255;
    j->g = 0;
    j->b = 0;
}

ফলাফল:

# ./vector 
UseArray completed in 3.264 seconds
UseVector completed in 5.443 seconds

না, আলাদা কিছু নয়। কমপক্ষে এটি ধীর হয় না। আমি ভেবেছিলাম এটিতে # 2 এর মতো পারফরম্যান্স থাকবে যেখানে আমি একটি Pixel&রেফারেন্স ব্যবহার করেছি ।

উপসংহার

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

নীচের লাইনটি হ'ল কম্পাইলারটি যখন ব্যবহার করছেন তখন অপ-ডিফল্ট কনস্ট্রাক্টর কলগুলি অপ্টিমাইজ করতে অক্ষম std::vector। আপনি যদি প্লেইন ব্যবহার করেন new[]তবে এগুলি ঠিক জরিমানা করে। তবে সাথে নেই std::vector। এমনকি আপনি যদি চারপাশে মন্ত্রটির মুখে উড়ে আসা কনস্ট্রাক্টর কলগুলি মুছে ফেলার জন্য আপনার কোডটি পুনরায় লিখতে পারেন: "সংকলকটি আপনার চেয়ে বেশি স্মার্ট The এসটিএল সাদামাটা সি এর মতোই দ্রুত is এটি সম্পর্কে চিন্তা করবেন না" "


2
আবার, কোডটি চালানোর জন্য ধন্যবাদ। যখন কেউ জনপ্রিয় মতামতকে চ্যালেঞ্জ করার চেষ্টা করে তখন কারণ ছাড়াই বাশাদ হওয়া সহজ।
kizzx2

3
"সমস্ত সি ++ নেসটি অপ্টিমাইজ করার জন্য এবং এসটিএল পাত্রে কাঁচা অ্যারে হিসাবে দ্রুততর করার জন্য যথেষ্ট পরিমাণে সংকলকটির স্মার্ট হওয়ার জন্য এটি অনেক বেশি।" ভাল মন্তব্য। আমার একটি তত্ত্ব আছে যে এই "সংকলকটি স্মার্ট" একটি মিথ মাত্র - সি ++ পার্সিং অত্যন্ত শক্ত এবং সংকলকটি কেবল একটি যন্ত্র।
kizzx2

3
আমি জানিনা. অবশ্যই, তিনি অ্যারে পরীক্ষার গতি কমিয়ে আনতে সক্ষম হয়েছিলেন , তবে তিনি ভেক্টরটিকে দ্রুততর করেননি । আমি উপরে সম্পাদনা করেছি যেখানে আমি পিক্সেল থেকে নির্মাতাদের সরিয়ে এটিকে একটি সহজ কাঠামো তৈরি করেছি এবং এটি এখনও ধীর ছিল। যারা সহজ প্রকারের মতো ব্যবহার করেন তাদের পক্ষে এটি খারাপ সংবাদ vector<int>
জন কুগেলম্যান

2
আমি আশা করি আমি সত্যিই আপনার উত্তর দুটি বার upvote করতে পারে। চেষ্টা করার মতো স্মার্ট আইডিয়া (যদিও বাস্তবে কেউ কাজ করেনি) যা আমি ভাবতেও পারি না!
kizzx2

9
কেবল একটি নোট রাখতে চেয়েছিলেন যে সি ++ পার্সিংয়ের জটিলতার (যা অত্যন্ত জটিল, হ্যাঁ) অপ্টিমাইজেশনের মানের সাথে কোনও সম্পর্ক নেই। পরেরটি সাধারণত এমন পর্যায়ে ঘটে যেখানে পার্স ফলাফল ইতিমধ্যে অনেকবার আরও অনেক নিম্ন-স্তরের প্রতিনিধিত্বতে রূপান্তরিত হয়।
পাভেল মিনায়েভ

44

এটি একটি পুরানো তবে জনপ্রিয় প্রশ্ন।

এই মুহুর্তে, অনেক প্রোগ্রামার সি ++ 11 এ কাজ করবে। এবং সি ++ 11 এ লিখিত হিসাবে ওপির কোডটি সমানভাবে দ্রুত UseArrayবা এর জন্য চালিত হয় UseVector

UseVector completed in 3.74482 seconds
UseArray completed in 3.70414 seconds

মৌলিক সমস্যাটি হ'ল যখন আপনার Pixelকাঠামোটি অবিচ্ছিন্ন করার std::vector<T>::resize( size_t, T const&=T() )সময় একটি ডিফল্ট তৈরি হয়েছিল Pixelএবং এটি অনুলিপি করে । সংকলকটি খেয়াল করেনি যে এটি অস্বীকারহীন ডেটা অনুলিপি করতে বলা হচ্ছে, সুতরাং এটি অনুলিপিটি সম্পাদন করেছে।

সি ++ 11 std::vector<T>::resizeএ দুটি ওভারলোড রয়েছে। প্রথমটি std::vector<T>::resize(size_t), অন্যটি std::vector<T>::resize(size_t, T const&)। এর অর্থ যখন আপনি resizeদ্বিতীয় যুক্তি ছাড়াই প্রার্থনা করেন , এটি কেবলমাত্র ডিফল্ট তৈরি হয় এবং সংকলকটি বুঝতে যথেষ্ট স্মার্ট হয় যে ডিফল্ট নির্মাণ কিছুই করে না, সুতরাং এটি বাফারের উপর দিয়ে চলে যায়।

(দুটি ওভারলোড যেখানে অস্থাবর, নির্মাণযোগ্য এবং অনুলিপিযোগ্য প্রকারগুলি পরিচালনা করতে যুক্ত করা হয়েছে - অবিচ্ছিন্ন ডেটাতে কাজ করার সময় পারফরম্যান্স উন্নতি করা একটি বোনাস)।

push_backসমাধান এছাড়াও fencepost পরীক্ষণ, যা এটি গতি নিচে, তাই এটি তুলনায় ধীর রয়ে করে mallocসংস্করণ।

লাইভ উদাহরণ (আমি এর সাথে টাইমারও প্রতিস্থাপন করেছি chrono::high_resolution_clock)।

মনে রাখবেন যে আপনার যদি এমন কাঠামো থাকে যা সাধারণত সূচনা প্রয়োজন, তবে আপনি আপনার বাফার বাড়ানোর পরে এটি পরিচালনা করতে চান, আপনি এটি একটি কাস্টম std::vectorবরাদ্দকারী দিয়ে করতে পারেন । আপনি যদি এটিকে আরও সাধারণের দিকে নিয়ে যেতে চান তবে std::vectorআমি বিশ্বাস করি যে সাবধানতার সাথে ব্যবহার করা allocator_traitsএবং ওভাররাইড করা এটিকে ==টানতে পারে তবে আমি অনিশ্চিত।


এখানে emplace_backবনাম কী করে তা দেখতে আকর্ষণীয়ও হবে push_back
ড্যানিয়েল

1
আমি আপনার ফলাফল পুনরুত্পাদন করতে পারবেন না। আপনার কোড কম্পাইল clang++ -std=c++11 -O3হয়েছে UseArray completed in 2.02e-07 secondsএবং UseVector completed in 1.3026 seconds। আমি UseVectorEmplaceBackপ্রায় একটি সংস্করণ যুক্ত করেছি । 2.5x হিসাবে দ্রুত UseVectorPushBack
ড্যানিয়েল

1
@ ড্যানিয়েল বিজোড়গুলি হ'ল অপ্টিমাইজারটি অ্যারের সংস্করণ থেকে সমস্ত কিছু সরিয়ে ফেলে। সর্বদা মাইক্রো বেনমার্ক সহ ঝুঁকি থাকে।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

4
হ্যাঁ আপনি ঠিক বলেছেন, কেবল সমাবেশটি দেখুন (বা এর অভাব) .. সম্ভবত that 6448514x পার্থক্যটি এটাই মনে করা উচিত ছিল! আমি অবাক হয়েছি কেন ভেক্টর সংস্করণ একই অপটিমাইজেশন করতে পারে না .. এটি যদি পুনরায় আকারের পরিবর্তে মাত্রা দিয়ে নির্মিত হয় তবে তা করে।
ড্যানিয়েল

34

ন্যায়সঙ্গত হওয়ার জন্য, আপনি একটি সি বাস্তবায়নের সাথে একটি সি ++ বাস্তবায়ন তুলনা করতে পারবেন না, কারণ আমি আপনার ম্যালোক সংস্করণটি বলব। malloc বস্তু তৈরি করে না - এটি কেবল কাঁচা মেমরি বরাদ্দ করে। তারপরে আপনি সেই স্মৃতিটিকে কনস্ট্রাক্টরকে কল না করে অবজেক্ট হিসাবে বিবেচনা করবেন না এটি সি +++++++++++++++++++++++++++++++ সম্ভবত সম্ভবত আপনি অবৈধ - আমি ভাষা আইনজীবীদের কাছে রেখে দেব)।

এটি বলেছিল, কেবলমাত্র ম্যালোককে new Pixel[dimensions*dimensions]বিনামূল্যে এবং নিখরচায় পরিবর্তন করা আপনার delete [] pixelsকাছে থাকা পিক্সেলের সরল বাস্তবায়নের সাথে খুব বেশি পার্থক্য করে না। আমার বাক্সে ফলাফল এখানে রয়েছে (E6600, 64-বিট):

UseArray completed in 0.269 seconds
UseVector completed in 1.665 seconds
UseVectorPushBack completed in 7.309 seconds
The whole thing completed in 9.244 seconds

তবে কিছুটা পরিবর্তন হলে, টেবিলগুলি ঘুরে:

Pixel.h

struct Pixel
{
    Pixel();
    Pixel(unsigned char r, unsigned char g, unsigned char b);

    unsigned char r, g, b;
};

Pixel.cc

#include "Pixel.h"

Pixel::Pixel() {}
Pixel::Pixel(unsigned char r, unsigned char g, unsigned char b) 
  : r(r), g(g), b(b) {}

main.cc

#include "Pixel.h"
[rest of test harness without class Pixel]
[UseArray now uses new/delete not malloc/free]

এইভাবে সংকলিত:

$ g++ -O3 -c -o Pixel.o Pixel.cc
$ g++ -O3 -c -o main.o main.cc
$ g++ -o main main.o Pixel.o

আমরা খুব ভিন্ন ফলাফল পেতে:

UseArray completed in 2.78 seconds
UseVector completed in 1.651 seconds
UseVectorPushBack completed in 7.826 seconds
The whole thing completed in 12.258 seconds

পিক্সেলের জন্য একটি অ-ইনলাইনড কনস্ট্রাক্টর সহ, স্টাড :: ভেক্টর এখন একটি কাঁচা অ্যারে ধরে।

এটি প্রদর্শিত হবে যে এসটিডি :: ভেক্টর এবং এসটিডি মাধ্যমে বরাদ্দ জটিলতা: বরাদ্দকরণ খুব বেশী হয় একটি সহজ হিসাবে হিসাবে কার্যকরভাবে অপ্টিমাইজ করা new Pixel[n]। তবে, আমরা দেখতে পাচ্ছি যে সমস্যাটি কেবল বরাদ্দ নিয়ে ভ্যাক্টর অ্যাক্সেসের সাথে নয়, একবার লুপের বাইরে চলে গিয়ে ভেক্টর / অ্যারে তৈরির জন্য কয়েকটি পরীক্ষামূলক ফাংশন টুইট করে:

void UseVector()
{
    TestTimer t("UseVector");

    int dimension = 999;
    std::vector<Pixel> pixels;
    pixels.resize(dimension * dimension);

    for(int i = 0; i < 1000; ++i)
    {
        for(int i = 0; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }
    }
}

এবং

void UseArray()
{
    TestTimer t("UseArray");

    int dimension = 999;
    Pixel * pixels = new Pixel[dimension * dimension];

    for(int i = 0; i < 1000; ++i)
    {
        for(int i = 0 ; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }
    }
    delete [] pixels;
}

আমরা এখন এই ফলাফলগুলি পেয়েছি:

UseArray completed in 0.254 seconds
UseVector completed in 0.249 seconds
UseVectorPushBack completed in 7.298 seconds
The whole thing completed in 7.802 seconds

এটি থেকে আমরা যা শিখতে পারি তা হল std :: ভেক্টর অ্যাক্সেসের জন্য কোনও কাঁচা অ্যারের সাথে তুলনীয় তবে আপনাকে যদি অনেকবার ভেক্টর / অ্যারে তৈরি করতে এবং মুছতে হয় তবে একটি জটিল অবজেক্ট তৈরি করা আরও সময় ব্যয় করে যে একটি সহজ অ্যারে তৈরি করে যখন উপাদানটির কনস্ট্রাক্টর অন্তর্ভুক্ত থাকে না। আমি মনে করি না যে এটি খুব অবাক।


3
আপনার এখনও একটি ইনিল্যান্ড কনস্ট্রাক্টর রয়েছে - অনুলিপি নির্মাণকারী।
বেন ভয়েগট

26

এটি দিয়ে চেষ্টা করুন:

void UseVectorCtor()
{
    TestTimer t("UseConstructor");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel> pixels(dimension * dimension, Pixel(255, 0, 0));
    }
}

আমি প্রায় ঠিক একই পারফরম্যান্স অ্যারের সাথে পেতে।

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

এবং আপনি যখন এটি ব্যবহার করেন, ভেক্টরটি অ্যারের মতোই দ্রুত হয়।

সুতরাং না, আপনি পারফরম্যান্সের পৌরাণিক কাহিনীটি ভাবেন নি। তবে আপনি দেখিয়েছেন যে কেবলমাত্র ভেক্টরকে সর্বোত্তমভাবে ব্যবহার করা হলে এটি সত্য true :)

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


1
আমি এখনও প্রশ্ন করি যে এটি একটি ন্যায্য তুলনা। আপনি যদি অভ্যন্তরীণ লুপ থেকে মুক্তি পেয়ে থাকেন তবে অ্যারের সমতুল্য হ'ল একটি একক পিক্সেল অবজেক্ট তৈরি করা এবং তারপরে এটি পুরো অ্যারে জুড়ে ব্লিটি করা।
জন কুগেলম্যান

1
ব্যবহার new[]একই ডিফল্ট নির্মাণগুলি করে যা vector.resize()তবুও এটি অনেক দ্রুত। new[]অভ্যন্তরীণ লুপটি + অভ্যন্তরীণ লুপের মতো একই গতি হওয়া উচিত vector.resize(), তবে এটি এটি প্রায় দ্বিগুণ দ্রুত।
জন কুগেলম্যান

@ জন: এটি ন্যায্য তুলনা। মূল কোডে, এরে সঙ্গে বরাদ্দ করা হয় malloc, যা আরম্ভ করে না অথবা কনস্ট্রাক্ট কিছু তাই এটি হল কার্যকরভাবে একটি একক পাস অ্যালগরিদম শুধু আমার মত vectorনমুনা। এবং new[]উত্তর হিসাবে স্পষ্টতই যে উভয় দুটি পাস প্রয়োজন, কিন্তু new[]ক্ষেত্রে, সংকলক অতিরিক্ত অতিরিক্ত ওভারহেড দূরে অপ্টিমাইজ করতে সক্ষম, যা ক্ষেত্রে এটি না করে vector। তবে আমি দেখছি না কেন সাবওটিমাল ক্ষেত্রে এটি আকর্ষণীয় হয়। আপনি যদি পারফরম্যান্সের বিষয়ে যত্নশীল হন তবে আপনি কোডটি লিখেন না।
জলফ

@ জন: আকর্ষণীয় মন্তব্য। আমি যদি পুরো অ্যারে জুড়ে অন্ধ করতে চেয়েছিলাম তবে আমার ধারণা অ্যারে আবার অনুকূল সমাধান - যেহেতু আমি vector::resize()অযথা কনস্ট্রাক্টরকে ফোন না করে সময় নষ্ট না করে আমাকে মেমরির সংকোচনাত্মক অংশ দিতে বলতে পারি না ।
kizzx2

@ কিজক্স 2: হ্যাঁ এবং না। একটি অ্যারে সাধারণত সি ++ তেও শুরু হয়। সি-তে, আপনি এমন ব্যবহার করবেন mallocযা প্রাথমিককরণ সম্পাদন করে না তবে এটি সি -+ তে নন-পিওডি প্রকারের সাথে কাজ করবে না। সুতরাং সাধারণ ক্ষেত্রে, একটি সি ++ অ্যারে ঠিক তত খারাপ। সম্ভবত প্রশ্নটি হ'ল, যদি আপনি প্রায়শই এই ব্লিটিং সম্পাদন করতে চলেছেন তবে আপনি কি একই অ্যারে / ভেক্টরটিকে পুনরায় ব্যবহার করবেন না? এবং যদি আপনি এটি করেন, তবে আপনি কেবল একবার "নিরর্থক নির্মাতারা" এর ব্যয় একেবারে শুরুতে প্রদান করুন। আসল উল্লাস সব পরে ঠিক হিসাবে দ্রুত।
jalf

22

আমি যখন আপনার কোডটি প্রথম দেখলাম তখন এটি মোটামুটি ন্যায্য তুলনা ছিল; আমি অবশ্যই ভেবেছিলাম আপনি আপেলের সাথে আপেলের তুলনা করছেন না। সুতরাং আমি ভেবেছিলাম, আসুন সমস্ত পরীক্ষার জন্য কন্সট্রাক্টর এবং ডেস্ট্রাক্টরদের ডাকা হচ্ছে; এবং তারপরে তুলনা করুন।

const size_t dimension = 1000;

void UseArray() {
    TestTimer t("UseArray");
    for(size_t j = 0; j < dimension; ++j) {
        Pixel* pixels = new Pixel[dimension * dimension];
        for(size_t i = 0 ; i < dimension * dimension; ++i) {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = (unsigned char) (i % 255);
        }
        delete[] pixels;
    }
}

void UseVector() {
    TestTimer t("UseVector");
    for(size_t j = 0; j < dimension; ++j) {
        std::vector<Pixel> pixels(dimension * dimension);
        for(size_t i = 0; i < dimension * dimension; ++i) {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = (unsigned char) (i % 255);
        }
    }
}

int main() {
    TestTimer t1("The whole thing");

    UseArray();
    UseVector();

    return 0;
}

আমার ধারণা ছিল, এই সেটআপের সাথে, তাদের ঠিক একই হওয়া উচিত । দেখা যাচ্ছে, আমি ভুল ছিলাম।

UseArray completed in 3.06 seconds
UseVector completed in 4.087 seconds
The whole thing completed in 10.14 seconds

তাহলে কেন এই 30% পারফরম্যান্স ক্ষতি হ'ল? এসটিএলে শিরোনামে সমস্ত কিছু রয়েছে, তাই সংকলকটির জন্য প্রয়োজনীয় সমস্ত কিছু বোঝা সম্ভব হয়েছিল।

আমার ধারণা ছিল লুপটি কীভাবে সমস্ত মানকে ডিফল্ট কনস্ট্রাক্টরের কাছে আরম্ভ করে। সুতরাং আমি একটি পরীক্ষা করেছি:

class Tester {
public:
    static int count;
    static int count2;
    Tester() { count++; }
    Tester(const Tester&) { count2++; }
};
int Tester::count = 0;
int Tester::count2 = 0;

int main() {
    std::vector<Tester> myvec(300);
    printf("Default Constructed: %i\nCopy Constructed: %i\n", Tester::count, Tester::count2);

    return 0;
}

ফলাফল হিসাবে আমি সন্দেহ ছিল:

Default Constructed: 1
Copy Constructed: 300

এটি স্পষ্টতই মন্দার উত্স, সত্য যে ভেক্টর একটি অনুলিপি তৈরির বস্তু থেকে উপাদানগুলির সূচনা করতে কপি নির্মাণকারী ব্যবহার করে uses

এর অর্থ, ভেক্টর নির্মাণের সময় নিম্নলিখিত সিউডো-অপারেশন আদেশটি ঘটছে:

Pixel pixel;
for (auto i = 0; i < N; ++i) vector[i] = pixel;

যা, সংকলক দ্বারা তৈরি অন্তর্ভুক্ত অনুলিপি অনুলিপি কারণে, নিম্নলিখিতটিতে প্রসারিত:

Pixel pixel;
for (auto i = 0; i < N; ++i) {
    vector[i].r = pixel.r;
    vector[i].g = pixel.g;
    vector[i].b = pixel.b;
}

ডিফল্ট সুতরাং Pixel, অ-ইনিশিয়ালাইজ রয়ে বাকিরা ইনিশিয়ালাইজ হয় ডিফল্ট সঙ্গে Pixelএর আন-ইনিশিয়ালাইজ মান।

New[]/ Delete[]: এর সাথে বিকল্প পরিস্থিতির তুলনায়

int main() {
    Tester* myvec = new Tester[300];

    printf("Default Constructed: %i\nCopy Constructed:%i\n", Tester::count, Tester::count2);

    delete[] myvec;

    return 0;
}

Default Constructed: 300
Copy Constructed: 0

এগুলি সমস্তগুলি তাদের অ-আরম্ভ করা মানগুলিতে ছেড়ে যায় এবং ক্রমের উপরে দ্বিগুণ পুনরাবৃত্তি ছাড়াই।

এই তথ্য সজ্জিত, আমরা কীভাবে এটি পরীক্ষা করতে পারি? আসুন অনুলিপি অনুলিপি নির্মাণকারীর অতিরিক্ত লেখার চেষ্টা করুন।

Pixel(const Pixel&) {}

এবং ফলাফল?

UseArray completed in 2.617 seconds
UseVector completed in 2.682 seconds
The whole thing completed in 5.301 seconds

সুতরাং সংক্ষেপে আপনি যদি প্রায়শই শত শত ভেক্টর তৈরি করেন: আপনার অ্যালগরিদমটি পুনরায় চিন্তা করুন

যাই হোক না কেন, কোনও অজানা কারণে এসটিএল বাস্তবায়ন ধীর নয়, এটি আপনি যা চান ঠিক তা করে; আশা করি আপনি আরও ভাল জানেন


3
আমরা (আপনি এবং আমি এবং এখানে অন্যান্য স্মার্ট লোক) যে মজা পেয়েছি তা বিচার করে এসটিএল বাস্তবায়নের "আশা" প্রকৃতপক্ষে বরং দাবিদার একটি: পি মূলত, আমরা অতিরঞ্জিত করতে পারি এবং এই সিদ্ধান্তে পৌঁছাতে পারি যে এটি আশা করে যে আমি এর উত্সটি পড়েছি এবং বিশ্লেষণ করেছি কোড। যাইহোক: পি
কিজক্স 2

1
অসাধারণ সব! ভিএস 2013 এ এটি অ্যারেগুলির তুলনায় ভেক্টরটিকে দ্রুত তৈরি করেছে। যদিও মনে হয় যে পারফরম্যান্স সমালোচনামূলক সিস্টেমগুলির জন্য আপনাকে এটি কার্যকরভাবে ব্যবহার করতে সক্ষম হওয়ার জন্য এসটিএলকে অনেকগুলি পরীক্ষা করা দরকার।
রোজিনা

7

চেক করা পুনরুক্তিকারীদের অক্ষম করার এবং মুক্তির মোডে বিল্ডিংয়ের চেষ্টা করুন। পারফরম্যান্সের পার্থক্যটি আপনার খুব বেশি দেখা উচিত নয়।


1
চেষ্টা করেছেন #define _SECURE_SCL 0। এটি UseVectorপ্রায় 4 সেকেন্ডের কোথাও তৈরি করেছিল ( gccনীচের মতো) তবে এটি এখনও দ্বিগুণ ধীর হয়ে গেছে।
kizzx2

এটি প্রায় অবশ্যই কারণ। মাইক্রোসফ্ট দয়া করে আমাদের ডিবাগ এবং রিলিজ উভয়ের জন্যই ডিফল্টরূপে ডিট্রেট ডিবাগ করে ite আমরা এটি ২০০৩ থেকে ২০০৮ সাল পর্যন্ত আপগ্রেড করার পরে ব্যাপক ধীরগতির মূল কারণ হিসাবে খুঁজে পেয়েছি visual ভিজ্যুয়াল স্টুডিওর মধ্যে সবচেয়ে মারাত্মক গ্যাচাছা অবশ্যই একটি।
ডগ টি।

2
@ কিজএক্সএক্স 2 অক্ষম করার জন্য আরও একটি ম্যাক্রো রয়েছে: HAS_ITERATOR_DEBUGGING বা এরকম কিছু।
ডগ টি।

যেমন @ মার্টিন এবং আমার উত্তরগুলি দেখায়, জিসিসি একই প্যাটার্নটি দেখায় এমনকি অপ্টিমাইজেশান সহ -O3
জন কুগেলম্যান

1
@Doug: ডক এ খুঁজছি, আমি মনে করি _HAS_ITERATOR_DEBUGGINGমুক্তি বিল্ড এ অক্ষম হল: msdn.microsoft.com/en-us/library/aa985939(VS.80).aspx
kizzx2

4

জিএনইউয়ের এসটিএল (এবং অন্যান্য) প্রদত্ত vector<T>(n), ডিফল্ট একটি প্রোটোটাইপাল অবজেক্ট তৈরি করে T()- সংকলক খালি নির্মাতাকে অপ্টিমাইজ করবে - তবে তারপরে অবজেক্টের জন্য সংরক্ষিত মেমরি ঠিকানার মধ্যে যা কিছু আবর্জনা ঘটেছে তার একটি অনুলিপি এসটিএল গ্রহণ করেছে __uninitialized_fill_n_aux, যা ভেক্টরের ডিফল্ট মান হিসাবে that বস্তুর অনুলিপিগুলি লোপ করে সুতরাং, "আমার" এসটিএলটি নির্মাণ লুপিং নয়, তবে লুপ / ​​অনুলিপি নির্মাণ করছে। এটি পাল্টা স্বজ্ঞাত, তবে আমি এই মুহূর্তটি সম্পর্কে সাম্প্রতিক স্ট্যাকওভারফ্লো প্রশ্নটিতে মন্তব্য করার সাথে আমার মনে রাখা উচিত: কনস্ট্রাক্ট / অনুলিপি রেফারেন্স গণনা করা অবজেক্টের জন্য আরও কার্যকর হতে পারে ইত্যাদি।

তাই:

vector<T> x(n);

অথবা

vector<T> x;
x.resize(n);

অনেকগুলি এসটিএল বাস্তবায়নের ক্ষেত্রে - এরকম কিছু:

T temp;
for (int i = 0; i < n; ++i)
    x[i] = temp;

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

এটি আরও সুস্পষ্ট, প্রত্যক্ষ বাস্তবায়নের সাথে বিপরীত হতে পারে:

for (int i = 0; i < n; ++i)
    x[i] = T();

যা আমরা কোনও সংকলকটি অপ্টিমাইজ করার আশা করতে পারি।

ভেক্টরের আচরণের এই দিকটির ন্যায্যতা সম্পর্কে কিছুটা স্পষ্ট হওয়ার জন্য, বিবেচনা করুন:

std::vector<big_reference_counted_object> x(10000);

স্পষ্টতই এটি একটি বড় পার্থক্য যদি আমরা একই ডেটা উল্লেখ করে 10000 বনাম 10000 স্বাধীন অবজেক্ট তৈরি করি। একটি যুক্তিসঙ্গত যুক্তি রয়েছে যে নৈমিত্তিক সি ++ ব্যবহারকারীদের দুর্ঘটনাক্রমে এত ব্যয়বহুল কিছু করা থেকে সুরক্ষিত করার সুবিধাটি অনুলিপি-অনুলিপি কপি নির্মাণের জন্য খুব ছোট বাস্তব-বিশ্বের ব্যয়কে ছাড়িয়ে যায়।

মূল উত্তর (রেফারেন্স / মন্তব্য বোঝার জন্য): কোন সুযোগ নেই। ভেক্টর একটি অ্যারের মতো দ্রুততর, কমপক্ষে যদি আপনি সংবেদনশীলভাবে স্থান সংরক্ষণ করেন। ...


6
আমি এই উত্তরটি যে কারও পক্ষে সামান্য উপযোগী হওয়া সত্যই প্রমাণ করতে পারি না। আমি আশা করি আমি দুবার ডাউন ডাউন করতে পারব।
kizzx2

-1, kizzx2 এ আমার সমর্থন আছে। ভেক্টর এটি সরবরাহ করে অতিরিক্ত বৈশিষ্ট্য, মহাবিশ্বের নিয়মের কারণে অ্যারের হিসাবে তত দ্রুত হতে পারে না, সমস্ত কিছুরই দাম রয়েছে!
ইয়েনফেই

আপনি মিস করছেন, টনি ... এটি একটি কৃত্রিম মানদণ্ডের উদাহরণ, তবে এটি কী পরিকল্পনা করে তা প্রমাণ করে।
পোটোটোওয়াটার

গোলাপ সবুজ, ভায়োলেট কমলা, ডাউনভোটগুলি তিক্ত, তবে উত্তর তাদের কাছে যায়।
পাভেল মিনায়েভ

3

মার্টিন ইয়র্ক এর উত্তর আমাকে বিরক্ত করেছে কারণ মনে হচ্ছে কার্পেটের নীচে ইনিশিয়েশন সমস্যাটি ব্রাশ করার চেষ্টা। তবে তিনি অনর্থক ডিফল্ট নির্মাণকে পারফরম্যান্স সমস্যার উত্স হিসাবে চিহ্নিত করার পক্ষে সঠিক।

[সম্পাদনা: মার্টিনের উত্তর আর ডিফল্ট নির্মাতা পরিবর্তন করার পরামর্শ দেয় না]]

হাতের কাছে তাত্ক্ষণিক সমস্যার জন্য, আপনি অবশ্যই এর vector<Pixel>পরিবর্তে কর্টারের 2-প্যারামিটার সংস্করণটি কল করতে পারেন :

std::vector<Pixel> pixels(dimension * dimension, Pixel(255, 0, 0));

যদি আপনি একটি ধ্রুবক মান দিয়ে আরম্ভ করতে চান তবে এটি কাজ করে, যা সাধারণ ঘটনা। তবে আরও সাধারণ সমস্যাটি হ'ল: আপনি কীভাবে দক্ষতার সাথে ধ্রুবক মানের চেয়ে জটিল কিছু দিয়ে প্রাথমিকভাবে শুরু করতে পারেন?

এর জন্য আপনি একটি ব্যবহার করতে পারেন back_insert_iteratorযা একটি পুনরাবৃত্তকারী অ্যাডাপ্টার। এর ভেক্টরের সাথে এখানে একটি উদাহরণ দেওয়া আছে int, যদিও সাধারণ ধারণাটি Pixelএস এর জন্য ঠিক একইভাবে কাজ করে :

#include <iterator>
// Simple functor return a list of squares: 1, 4, 9, 16...
struct squares {
    squares() { i = 0; }
    int operator()() const { ++i; return i * i; }

private:
    int i;
};

...

std::vector<int> v;
v.reserve(someSize);     // To make insertions efficient
std::generate_n(std::back_inserter(v), someSize, squares());

বিকল্পভাবে আপনি copy()বা এর transform()পরিবর্তে ব্যবহার করতে পারেন generate_n()

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


2

ভেক্টরগুলি অতিরিক্তভাবে পিক্সেল কনস্ট্রাক্টরকে কল করছে।

প্রতিটি আপনার সময়সাপেক্ষে প্রায় এক মিলিয়ন সিটার রান তৈরি করে।

সম্পাদনা করুন: তারপরে বাহ্যিক 1 ... 1000 লুপ রয়েছে, সুতরাং এটি একটি বিলিয়ন সেন্টার কল করুন!

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


আপনি ঠিক বলেছেন, তবে প্রশ্নটি হল: কীভাবে এই অর্থহীন কর্টর কলগুলি বন্ধ করা যায়? এটি নন-এসটিএল পদ্ধতির পক্ষে সহজ, তবে এসটিএল পথের পক্ষে কঠিন / কুশ্রী।
j_random_hacker

1

push_backভেক্টরের পদ্ধতিটি কীভাবে কাজ করে তা এখানে:

  1. ভেক্টর যখন এটি শুরু করা হয় তখন এক্স পরিমাণ পরিমাণ বরাদ্দ করে।
  2. এটি নীচে বর্ণিত হিসাবে আইটেমের জন্য বর্তমান অন্তর্নিহিত অ্যারেতে কোনও জায়গা আছে কিনা তা পরীক্ষা করে দেখুন।
  3. এটি পুশ_ব্যাক কলে আইটেমটির একটি অনুলিপি তৈরি করে।

push_backএক্স আইটেম কল করার পরে :

  1. ভেক্টর কেএক্স পরিমাণের স্থানটিকে ২ য় অ্যারেতে পুনরায় স্থান দেয়।
  2. এটি প্রথম অ্যারের এন্ট্রিগুলিকে দ্বিতীয়টিতে অনুলিপি করে।
  3. প্রথম অ্যারে বাদ দেয়।
  4. এটি কেএক্স এন্ট্রিতে পৌঁছা পর্যন্ত স্টোরেজ হিসাবে দ্বিতীয় অ্যারে ব্যবহার করে।

পদ্ধতি পুনরাবৃত্তি করুন। আপনি যদি reservingস্থান না হন তবে এটি অবশ্যই ধীর হতে চলেছে। এর চেয়ে বড় বিষয়, যদি আইটেমটি অনুলিপি করা ব্যয়বহুল হয় তবে এর মতো 'পুশ_ব্যাক' আপনাকে জীবিত খেতে চলেছে।

হিসেবে vectorবনাম অ্যারের জিনিস, আমি অন্যান্য লোকেদের সঙ্গে একমত আছে যাচ্ছি। মুক্তির জন্য চালান, অপ্টিমাইজেশন চালু করুন এবং আরও কয়েকটি পতাকা লাগান যাতে মাইক্রোসফ্টের বন্ধুত্বপূর্ণ লোকেরা এটি আপনার জন্য @ @% $ don't আপ না করে।

আর একটি বিষয়, আপনার যদি আকার পরিবর্তন করতে না হয় তবে বুস্ট.আররে ব্যবহার করুন।


আমি বুঝতে পেরেছি যে লোকেরা যখন ভারব্যাটিম পোস্ট করেন তখন প্রচুর কোড পড়তে পছন্দ করেন না। তবে আমার ব্যবহারের reserveমতো ব্যবহার করেছি did
kizzx2

দুঃখিত আমি এটি মিস করেছি। আমি সেখানে রাখা অন্য কিছুই কি আদৌ সহায়ক ছিল?
Wheaties

push_backধ্রুবক সময় amorised হয়েছে। দেখে মনে হচ্ছে আপনি কোনও ও (এন) প্রক্রিয়া বর্ণনা করছেন। (1 এবং 3 টি পদক্ষেপ পুরোপুরি জায়গা থেকে দূরে মনে হচ্ছে)) push_backওপির জন্য ধীরে ধীরে কী ঘটে তা হ'ল রিলোকেশনটি হওয়া দরকার কিনা তা পরীক্ষা করে newদেখানো হয় , পয়েন্টারগুলি আপডেট করা হয়, স্থান নির্ধারণের অভ্যন্তরে NULL এর বিরুদ্ধে চেক করা হয় এবং অন্যান্য ছোট ছোট জিনিস যা সাধারণত ডুবে যায় see প্রোগ্রামটির আসল কাজ।
পোটোটোওয়াটার

reserveএটি এখনও ধীরে ধীরে চলতে চলেছে যেহেতু এখনও এটি পরীক্ষা করে নিতে হয়েছে (এটি পুনরুদ্ধার করা দরকার কিনা) push_back
পাভেল মিনায়েভ

সমস্ত ভাল পয়েন্ট। আমি যে শব্দগুলি ও (এন) প্রক্রিয়াটির মতো বর্ণনা করছি তা কিন্তু তা নয়, বেশ যথেষ্ট নয়। আমি জানি বেশিরভাগ লোকেরা কীভাবে vectorএটির আকার পরিবর্তনশীল কার্যকারিতা সম্পাদন করে তা বোঝে না , এটি কেবল "যাদু"। এখানে, আমি এটি আরও কিছুটা পরিষ্কার করি let
Wheaties

1

কিছু প্রোফাইলার ডেটা (পিক্সেল 32 বিটের সাথে সংযুক্ত):

g++ -msse3 -O3 -ftree-vectorize -g test.cpp -DNDEBUG && ./a.out
UseVector completed in 3.123 seconds
UseArray completed in 1.847 seconds
UseVectorPushBack completed in 9.186 seconds
The whole thing completed in 14.159 seconds

বাজে কথা

andrey@nv:~$ opannotate --source libcchem/src/a.out  | grep "Total samples for file" -A3
Overflow stats not available
 * Total samples for file : "/usr/include/c++/4.4/ext/new_allocator.h"
 *
 * 141008 52.5367
 */
--
 * Total samples for file : "/home/andrey/libcchem/src/test.cpp"
 *
 *  61556 22.9345
 */
--
 * Total samples for file : "/usr/include/c++/4.4/bits/stl_vector.h"
 *
 *  41956 15.6320
 */
--
 * Total samples for file : "/usr/include/c++/4.4/bits/stl_uninitialized.h"
 *
 *  20956  7.8078
 */
--
 * Total samples for file : "/usr/include/c++/4.4/bits/stl_construct.h"
 *
 *   2923  1.0891
 */

ইন allocator:

               :      // _GLIBCXX_RESOLVE_LIB_DEFECTS
               :      // 402. wrong new expression in [some_] allocator::construct
               :      void
               :      construct(pointer __p, const _Tp& __val)
141008 52.5367 :      { ::new((void *)__p) _Tp(__val); }

vector:

               :void UseVector()
               :{ /* UseVector() total:  60121 22.3999 */
...
               :
               :
 10790  4.0201 :        for (int i = 0; i < dimension * dimension; ++i) {
               :
   495  0.1844 :            pixels[i].r = 255;
               :
 12618  4.7012 :            pixels[i].g = 0;
               :
  2253  0.8394 :            pixels[i].b = 0;
               :
               :        }

বিন্যাস

               :void UseArray()
               :{ /* UseArray() total:  35191 13.1114 */
               :
...
               :
   136  0.0507 :        for (int i = 0; i < dimension * dimension; ++i) {
               :
  9897  3.6874 :            pixels[i].r = 255;
               :
  3511  1.3081 :            pixels[i].g = 0;
               :
 21647  8.0652 :            pixels[i].b = 0;

ওভারহেডের বেশিরভাগটি অনুলিপি নির্মাণকারীতে থাকে। উদাহরণ স্বরূপ,

    std::vector < Pixel > pixels;//(dimension * dimension, Pixel());

    pixels.reserve(dimension * dimension);

    for (int i = 0; i < dimension * dimension; ++i) {

        pixels[i].r = 255;

        pixels[i].g = 0;

        pixels[i].b = 0;
    }

এটির অ্যারের মতোই পারফরম্যান্স রয়েছে।


2
দুর্ভাগ্যক্রমে, আপনি যে "সমাধান" দিয়েছেন pixels.size()তা ভেঙে যাবে।
kizzx2

1
এটি ভুল, আপনি রিজার্ভকে কল করতে পারবেন না এবং তারপরে উপাদানগুলি ব্যবহার করতে পারবেন না, আইটেমগুলি যুক্ত করতে আপনাকে অবশ্যই পুশ_ব্যাক ব্যবহার করতে হবে
পৌল

1

আমার ল্যাপটপটি লেনোভা জি 770 (4 জিবি র‌্যাম)।

ওএসটি উইন্ডোজ 7 64-বিট (ল্যাপটপ সহ একটি)

কম্পাইলার হয় MinGW 4.6.1।

আইডিই হ'ল কোড :: ব্লক

আমি প্রথম পোস্টের উত্স কোডগুলি পরীক্ষা করি।

ফলাফলগুলো

ও 2 অপটিমাইজেশন

UseArray 2.841 সেকেন্ডে সমাপ্ত হয়েছে

ইউজভেেক্টর 2.548 সেকেন্ডে সমাপ্ত

ইউজভেেক্টরপশব্যাক ১১.৯৯ সেকেন্ডের মধ্যে শেষ হয়েছে

পুরো জিনিসটি 17.342 সেকেন্ডে শেষ হয়েছে

সিস্টেম বিরতি

O3 অপ্টিমাইজেশন

UseArray 1.452 সেকেন্ডে সমাপ্ত হয়েছে

ইউজভেেক্টর 2.514 সেকেন্ডে সমাপ্ত

UseVectorPushBack 12.967 সেকেন্ডে শেষ হয়েছে

পুরো জিনিসটি 16.937 সেকেন্ডে শেষ হয়েছে

দেখে মনে হচ্ছে ওেক্টরটির কার্য সম্পাদন O3 অপ্টিমাইজেশনের অধীনে খারাপ।

আপনি যদি লুপটি পরিবর্তন করেন

    pixels[i].r = i;
    pixels[i].g = i;
    pixels[i].b = i;

O2 এবং O3 এর অধীনে অ্যারে এবং ভেক্টরের গতি প্রায় একই।


এমনকি আমি ও -৩ এর অধীনে প্রথম পরীক্ষার ক্ষেত্রে ভেল্টের পারফরম্যান্স অ্যারের তুলনায় এখনও ধীর গতির হয়ে যায় ut তবে আপনি যখন নির্ধারিত মানটি (255, 0, 0) থেকে (i, i, i) পরিবর্তন করেন তখন ভেক্টর এবং অ্যারে O2 এবং O3 এর অধীনে প্রায় একই, এটি বেশ অদ্ভুত
স্টেরিও ম্যাচিং

দুঃখিত, আমি মুছে ফেলতে বিনামূল্যে পরিবর্তন করতে ভুলে গিয়েছি delete মুছে ফ্রি পরিবর্তন করার পরে, ভেক্টর এবং অ্যারের O3 এর অধীনে পারফরম্যান্স এখন একই রকম, দেখে মনে হচ্ছে বরাদ্দকারীর মূল কারণ?
স্টিরিওম্যাচিং

1

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

$ g++ test.cpp -o test -O3 -march=native
$ ./test 
UseArray inner completed in 0.652 seconds
UseArray completed in 0.773 seconds
UseVector inner completed in 0.638 seconds
UseVector completed in 0.757 seconds
UseVectorPushBack inner completed in 6.732 seconds
UseVectorPush completed in 6.856 seconds
The whole thing completed in 8.387 seconds

কম্পাইলার:

gcc version 6.2.0 20161019 (Debian 6.2.0-9)

সিপিইউ:

model name  : Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz

এবং কোড:

#include <cstdlib>
#include <vector>

#include <iostream>
#include <string>

#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>

class TestTimer
{
    public:
        TestTimer(const std::string & name) : name(name),
            start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
        {
        }

        ~TestTimer()
        {
            using namespace std;
            using namespace boost;

            posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
            posix_time::time_duration d = now - start;

            cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
                " seconds" << endl;
        }

    private:
        std::string name;
        boost::posix_time::ptime start;
};

struct Pixel
{
    Pixel()
    {
    }

    Pixel(unsigned char r, unsigned char g, unsigned char b) : r(r), g(g), b(b)
    {
    }

    unsigned char r, g, b;
};

void UseVector(std::vector<std::vector<Pixel> >& results)
{
    TestTimer t("UseVector inner");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel>& pixels = results.at(i);
        pixels.resize(dimension * dimension);

        for(int i = 0; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }
    }
}

void UseVectorPushBack(std::vector<std::vector<Pixel> >& results)
{
    TestTimer t("UseVectorPushBack inner");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel>& pixels = results.at(i);
            pixels.reserve(dimension * dimension);

        for(int i = 0; i < dimension * dimension; ++i)
            pixels.push_back(Pixel(255, 0, 0));
    }
}

void UseArray(Pixel** results)
{
    TestTimer t("UseArray inner");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        Pixel * pixels = (Pixel *)malloc(sizeof(Pixel) * dimension * dimension);

        results[i] = pixels;

        for(int i = 0 ; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }

        // free(pixels);
    }
}

void UseArray()
{
    TestTimer t("UseArray");
    Pixel** array = (Pixel**)malloc(sizeof(Pixel*)* 1000);
    UseArray(array);
    for(int i=0;i<1000;++i)
        free(array[i]);
    free(array);
}

void UseVector()
{
    TestTimer t("UseVector");
    {
        std::vector<std::vector<Pixel> > vector(1000, std::vector<Pixel>());
        UseVector(vector);
    }
}

void UseVectorPushBack()
{
    TestTimer t("UseVectorPush");
    {
        std::vector<std::vector<Pixel> > vector(1000, std::vector<Pixel>());
        UseVectorPushBack(vector);
    }
}


int main()
{
    TestTimer t1("The whole thing");

    UseArray();
    UseVector();
    UseVectorPushBack();

    return 0;
}

1

আমি কিছু সময়ের জন্য যা চেয়েছিলাম তার কিছু বিস্তৃত পরীক্ষা করেছি। পাশাপাশি ভাগ করে নিতে পারেন।

এটি আমার ডুয়াল বুট মেশিন i7-3770, 16 জিবি র‌্যাম, x86_64, উইন্ডোজ 8.1 এবং উবুন্টু 16.04 এ। আরও তথ্য এবং উপসংহার, নীচে মন্তব্য। এমএসভিএস 2017 এবং জি ++ উভয়ই (উইন্ডোজ এবং লিনাক্সে উভয়) পরীক্ষিত।

পরীক্ষা প্রোগ্রাম

#include <iostream>
#include <chrono>
//#include <algorithm>
#include <array>
#include <locale>
#include <vector>
#include <queue>
#include <deque>

// Note: total size of array must not exceed 0x7fffffff B = 2,147,483,647B
//  which means that largest int array size is 536,870,911
// Also image size cannot be larger than 80,000,000B
constexpr int long g_size = 100000;
int g_A[g_size];


int main()
{
    std::locale loc("");
    std::cout.imbue(loc);
    constexpr int long size = 100000;  // largest array stack size

    // stack allocated c array
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    int A[size];
    for (int i = 0; i < size; i++)
        A[i] = i;

    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "c-style stack array duration=" << duration / 1000.0 << "ms\n";
    std::cout << "c-style stack array size=" << sizeof(A) << "B\n\n";

    // global stack c array
    start = std::chrono::steady_clock::now();
    for (int i = 0; i < g_size; i++)
        g_A[i] = i;

    duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "global c-style stack array duration=" << duration / 1000.0 << "ms\n";
    std::cout << "global c-style stack array size=" << sizeof(g_A) << "B\n\n";

    // raw c array heap array
    start = std::chrono::steady_clock::now();
    int* AA = new int[size];    // bad_alloc() if it goes higher than 1,000,000,000
    for (int i = 0; i < size; i++)
        AA[i] = i;

    duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "c-style heap array duration=" << duration / 1000.0 << "ms\n";
    std::cout << "c-style heap array size=" << sizeof(AA) << "B\n\n";
    delete[] AA;

    // std::array<>
    start = std::chrono::steady_clock::now();
    std::array<int, size> AAA;
    for (int i = 0; i < size; i++)
        AAA[i] = i;
    //std::sort(AAA.begin(), AAA.end());

    duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "std::array duration=" << duration / 1000.0 << "ms\n";
    std::cout << "std::array size=" << sizeof(AAA) << "B\n\n";

    // std::vector<>
    start = std::chrono::steady_clock::now();
    std::vector<int> v;
    for (int i = 0; i < size; i++)
        v.push_back(i);
    //std::sort(v.begin(), v.end());

    duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "std::vector duration=" << duration / 1000.0 << "ms\n";
    std::cout << "std::vector size=" << v.size() * sizeof(v.back()) << "B\n\n";

    // std::deque<>
    start = std::chrono::steady_clock::now();
    std::deque<int> dq;
    for (int i = 0; i < size; i++)
        dq.push_back(i);
    //std::sort(dq.begin(), dq.end());

    duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "std::deque duration=" << duration / 1000.0 << "ms\n";
    std::cout << "std::deque size=" << dq.size() * sizeof(dq.back()) << "B\n\n";

    // std::queue<>
    start = std::chrono::steady_clock::now();
    std::queue<int> q;
    for (int i = 0; i < size; i++)
        q.push(i);

    duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
    std::cout << "std::queue duration=" << duration / 1000.0 << "ms\n";
    std::cout << "std::queue size=" << q.size() * sizeof(q.front()) << "B\n\n";
}

ফলাফল

//////////////////////////////////////////////////////////////////////////////////////////
// with MSVS 2017:
// >> cl /std:c++14 /Wall -O2 array_bench.cpp
//
// c-style stack array duration=0.15ms
// c-style stack array size=400,000B
//
// global c-style stack array duration=0.130ms
// global c-style stack array size=400,000B
//
// c-style heap array duration=0.90ms
// c-style heap array size=4B
//
// std::array duration=0.20ms
// std::array size=400,000B
//
// std::vector duration=0.544ms
// std::vector size=400,000B
//
// std::deque duration=1.375ms
// std::deque size=400,000B
//
// std::queue duration=1.491ms
// std::queue size=400,000B
//
//////////////////////////////////////////////////////////////////////////////////////////
//
// with g++ version:
//      - (tdm64-1) 5.1.0 on Windows
//      - (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609 on Ubuntu 16.04
// >> g++ -std=c++14 -Wall -march=native -O2 array_bench.cpp -o array_bench
//
// c-style stack array duration=0ms
// c-style stack array size=400,000B
//
// global c-style stack array duration=0.124ms
// global c-style stack array size=400,000B
//
// c-style heap array duration=0.648ms
// c-style heap array size=8B
//
// std::array duration=1ms
// std::array size=400,000B
//
// std::vector duration=0.402ms
// std::vector size=400,000B
//
// std::deque duration=0.234ms
// std::deque size=400,000B
//
// std::queue duration=0.304ms
// std::queue size=400,000
//
//////////////////////////////////////////////////////////////////////////////////////////

মন্তব্য

  • গড়ে 10 রান সংগ্রহ করেছেন।
  • আমি প্রাথমিকভাবেও পরীক্ষা দিয়েছি std::sort()(আপনি এটির মন্তব্য দেখতে পাচ্ছেন) তবে পরে সেগুলি সরিয়েছি কারণ উল্লেখযোগ্য আপেক্ষিক পার্থক্য নেই।

আমার সিদ্ধান্ত এবং মন্তব্য

  • লক্ষ করুন যে কীভাবে বিশ্বব্যাপী সি-স্টাইলের অ্যারে হিপ সি-স্টাইল অ্যারের মতো প্রায় সময় নেয়
  • সমস্ত পরীক্ষার মধ্যে আমি std::arrayধারাবাহিক রানের মধ্যে সময়ের পরিবর্তনের মধ্যে একটি অসাধারণ স্থিতিশীলতা লক্ষ্য করেছি , অন্যরা বিশেষত স্টাড :: ডেটা স্ট্রাক্ট তুলনায় তুলনামূলকভাবে বিভিন্নভাবে পরিবর্তিত হয়েছে
  • O3 অপ্টিমাইজেশন কোনও উল্লেখযোগ্য সময়ের পার্থক্য দেখায় না
  • উইন্ডোজ ক্লিপ (নং -O2) এবং জি ++ (উইন / লিনাক্স নং -O2, নন-মার্চ = নেটিভ) এর উপর অপ্টিমাইজেশন অপসারণ করা চিহ্নকে স্বাক্ষর করে times বিশেষত স্টাডি :: ডেটা স্ট্রাক্টের জন্য। জি ++ এর চেয়ে এমএসভিএসে সামগ্রিকভাবে উচ্চতর সময়, std::arrayএবং সি-স্টাইলের বিন্যাসটি উইন্ডোজটিতে অপ্টিমাইজেশন ছাড়াই দ্রুততর হয়
  • g ++ মাইক্রোসফ্টের সংকলকটির চেয়ে দ্রুত কোড উত্পাদন করে (দৃশ্যত এটি উইন্ডোজেও দ্রুত চালিত হয়)।

রায়

অবশ্যই এটি একটি অপ্টিমাইজড বিল্ডের কোড। এবং প্রশ্নটি যেহেতু তখন ছিল std::vectorহ্যাঁ এটি অনেক বেশি! প্লেইন অ্যারেগুলির তুলনায় ধীর (অপ্টিমাইজড / অপরিশোধিত)। আপনি যখন একটি মানদণ্ড করছেন, আপনি স্বাভাবিকভাবেই অনুকূলিত কোড উত্পাদন করতে চান want

আমার জন্য শো এর তারকা যদিও হয়েছে std::array


0

সঠিক বিকল্পগুলির সাহায্যে ভেক্টর এবং অ্যারেগুলি অভিন্ন asm তৈরি করতে পারে । এই ক্ষেত্রে, তারা অবশ্যই একই গতি, কারণ আপনি যেভাবেই একই এক্সিকিউটেবল ফাইল পান get


1
এই ক্ষেত্রে, তারা একই বিধানসভা তৈরি করে বলে মনে হয় না। বিশেষত, ভেক্টর ব্যবহার করে কন্সট্রাক্টরদের কাছে কলটি দমন করার কোনও উপায় নেই বলে মনে হয়। আপনি সেই সমস্যার জন্য এখানে উত্তরগুলি উল্লেখ করতে পারেন (এটি একটি দীর্ঘ পঠিত তবে এটি আপনাকে বোঝায় যে লিঙ্কটিতে সরল পরীক্ষার কেস ছাড়া অন্য ক্ষেত্রে পারফরম্যান্সের পার্থক্য রয়েছে কেন তা ব্যাখ্যা করে)) (আসলে, এখানে কোনও উপায় বলে মনে হচ্ছে - - প্রস্তাবিত হিসাবে একটি কাস্টম এসটিএল বরাদ্দকারীর লেখা। ব্যক্তিগতভাবে, আমি এটি
ম্যালোক

1
@ kizzx2: আপনি ব্যবহার করতে যেমন লেন্থ যেতে হবে যে unconstructed বস্তু, একটি ভাল জিনিস কারণ এটি একটি ত্রুটি 99% এর সময় (আমি প্রবলভাবে underestimating হতে পারে)। আমি অন্যান্য উত্তরগুলি পড়েছি এবং আমি বুঝতে পেরেছি যে আমি আপনার নির্দিষ্ট পরিস্থিতির দিকে নজর দিচ্ছি না (প্রয়োজন নেই, অন্যান্য উত্তরগুলি সঠিক)) তবে আমি কীভাবে ভেক্টর এবং অ্যারে ঠিক একইরকম আচরণ করতে পারে তার এই উদাহরণটি আপনাকে সরবরাহ করতে চেয়েছিলাম।

@ রোজার: দারুণ! লিঙ্কটির জন্য ধন্যবাদ
kizzx2

0

উপায় দ্বারা ভেক্টর ব্যবহার করে ক্লাসগুলিতে আপনার দেখার কাজটি ধীরগতিতে প্রকারের মতো মানক প্রকারের সাথেও ঘটে। এখানে বহুবিবাহিত কোড:

#include <iostream>
#include <cstdio>
#include <map>
#include <string>
#include <typeinfo>
#include <vector>
#include <pthread.h>
#include <sstream>
#include <fstream>
using namespace std;

//pthread_mutex_t map_mutex=PTHREAD_MUTEX_INITIALIZER;

long long num=500000000;
int procs=1;

struct iterate
{
    int id;
    int num;
    void * member;
    iterate(int a, int b, void *c) : id(a), num(b), member(c) {}
};

//fill out viterate and piterate
void * viterate(void * input)
{
    printf("am in viterate\n");
    iterate * info=static_cast<iterate *> (input);
    // reproduce member type
    vector<int> test= *static_cast<vector<int>*> (info->member);
    for (int i=info->id; i<test.size(); i+=info->num)
    {
        //printf("am in viterate loop\n");
        test[i];
    }
    pthread_exit(NULL);
}

void * piterate(void * input)
{
    printf("am in piterate\n");
    iterate * info=static_cast<iterate *> (input);;
    int * test=static_cast<int *> (info->member);
    for (int i=info->id; i<num; i+=info->num) {
        //printf("am in piterate loop\n");
        test[i];
    }
    pthread_exit(NULL);
}

int main()
{
    cout<<"producing vector of size "<<num<<endl;
    vector<int> vtest(num);
    cout<<"produced  a vector of size "<<vtest.size()<<endl;
    pthread_t thread[procs];

    iterate** it=new iterate*[procs];
    int ans;
    void *status;

    cout<<"begining to thread through the vector\n";
    for (int i=0; i<procs; i++) {
        it[i]=new iterate(i, procs, (void *) &vtest);
    //  ans=pthread_create(&thread[i],NULL,viterate, (void *) it[i]);
    }
    for (int i=0; i<procs; i++) {
        pthread_join(thread[i], &status);
    }
    cout<<"end of threading through the vector";
    //reuse the iterate structures

    cout<<"producing a pointer with size "<<num<<endl;
    int * pint=new int[num];
    cout<<"produced a pointer with size "<<num<<endl;

    cout<<"begining to thread through the pointer\n";
    for (int i=0; i<procs; i++) {
        it[i]->member=&pint;
        ans=pthread_create(&thread[i], NULL, piterate, (void*) it[i]);
    }
    for (int i=0; i<procs; i++) {
        pthread_join(thread[i], &status);
    }
    cout<<"end of threading through the pointer\n";

    //delete structure array for iterate
    for (int i=0; i<procs; i++) {
        delete it[i];
    }
    delete [] it;

    //delete pointer
    delete [] pint;

    cout<<"end of the program"<<endl;
    return 0;
}

কোড থেকে আচরণটি দেখায় যে ভেক্টরের ইনস্ট্যান্টেশন হল কোডের দীর্ঘতম অংশ। একবার আপনি এই বোতল ঘাড় মাধ্যমে পেতে। কোডের বাকি অংশটি অত্যন্ত দ্রুত চলে। আপনি কতগুলি থ্রেড চালাচ্ছেন তা নির্ধারণ করা সত্য।

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


0

আমি কেবল উল্লেখ করতে চাই যে ভেক্টর (এবং স্মার্ট_পিটার) কেবল কাঁচা অ্যারে (এবং কাঁচা পয়েন্টার) এর উপরে একটি পাতলা স্তর যুক্ত add এবং আসলে অবিচ্ছিন্ন স্মৃতিতে কোনও ভেক্টরের অ্যাক্সেসের সময় অ্যারের চেয়ে দ্রুত হয় faster নিম্নলিখিত কোডটি ভেক্টর এবং অ্যারে আরম্ভ এবং অ্যাক্সেসের ফলাফল দেখায়।

#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
#include <vector>
#define SIZE 20000
int main() {
    srand (time(NULL));
    vector<vector<int>> vector2d;
    vector2d.reserve(SIZE);
    int index(0);
    boost::posix_time::ptime start_total = boost::posix_time::microsec_clock::local_time();
    //  timer start - build + access
    for (int i = 0; i < SIZE; i++) {
        vector2d.push_back(vector<int>(SIZE));
    }
    boost::posix_time::ptime start_access = boost::posix_time::microsec_clock::local_time();
    //  timer start - access
    for (int i = 0; i < SIZE; i++) {
        index = rand()%SIZE;
        for (int j = 0; j < SIZE; j++) {

            vector2d[index][index]++;
        }
    }
    boost::posix_time::ptime end = boost::posix_time::microsec_clock::local_time();
    boost::posix_time::time_duration msdiff = end - start_total;
    cout << "Vector total time: " << msdiff.total_milliseconds() << "milliseconds.\n";
    msdiff = end - start_acess;
    cout << "Vector access time: " << msdiff.total_milliseconds() << "milliseconds.\n"; 


    int index(0);
    int** raw2d = nullptr;
    raw2d = new int*[SIZE];
    start_total = boost::posix_time::microsec_clock::local_time();
    //  timer start - build + access
    for (int i = 0; i < SIZE; i++) {
        raw2d[i] = new int[SIZE];
    }
    start_access = boost::posix_time::microsec_clock::local_time();
    //  timer start - access
    for (int i = 0; i < SIZE; i++) {
        index = rand()%SIZE;
        for (int j = 0; j < SIZE; j++) {

            raw2d[index][index]++;
        }
    }
    end = boost::posix_time::microsec_clock::local_time();
    msdiff = end - start_total;
    cout << "Array total time: " << msdiff.total_milliseconds() << "milliseconds.\n";
    msdiff = end - start_acess;
    cout << "Array access time: " << msdiff.total_milliseconds() << "milliseconds.\n"; 
    for (int i = 0; i < SIZE; i++) {
        delete [] raw2d[i];
    }
    return 0;
}

আউটপুটটি হ'ল:

    Vector total time: 925milliseconds.
    Vector access time: 4milliseconds.
    Array total time: 30milliseconds.
    Array access time: 21milliseconds.

সুতরাং আপনি যদি এটি সঠিকভাবে ব্যবহার করেন তবে গতিটি প্রায় একই হবে। (অন্যরা যেমন রিজার্ভ () বা পুনরায় আকার () ব্যবহার করে উল্লিখিত হয়েছে)।


0

ওয়েল, কারণ ভেক্টর :: মাপদণ্ড () মাপসই মেমরির বরাদ্দের চেয়ে অনেক বেশি প্রক্রিয়াজাতকরণ করে (ম্যালোক দ্বারা)।

আপনার অনুলিপি নির্মাণকারীকে একটি ব্রেকপয়েন্ট স্থাপন করার চেষ্টা করুন (এটি সংজ্ঞায়িত করুন যাতে আপনি ব্রেকপয়েন্ট পয়েন্ট করতে পারেন!) এবং অতিরিক্ত প্রক্রিয়াজাতকরণের সময়টি সেখানে যায়।


0

আমাকে বলতে হবে যে আমি সি ++ তে বিশেষজ্ঞ নই। তবে কিছু পরীক্ষার ফলাফল যুক্ত করতে:

সংকলন: gcc-6.2.0 / বিন / জি ++ -O3 -std = সি ++ 14 ভেক্টর

মেশিন:

Intel(R) Xeon(R) CPU E5-2690 v2 @ 3.00GHz 

অপারেটিং সিস্টেম:

2.6.32-642.13.1.el6.x86_64

আউটপুট:

UseArray completed in 0.167821 seconds
UseVector completed in 0.134402 seconds
UseConstructor completed in 0.134806 seconds
UseFillConstructor completed in 1.00279 seconds
UseVectorPushBack completed in 6.6887 seconds
The whole thing completed in 8.12888 seconds

এখানে আমি কেবল অদ্ভুত বোধ করি "ইউজফিলকন্সট্রাক্টর" পারফরম্যান্স "ইউজকন্সট্রাক্টর" এর সাথে তুলনা করে।

কোড:

void UseConstructor()
{
    TestTimer t("UseConstructor");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel> pixels(dimension*dimension);
        for(int i = 0; i < dimension * dimension; ++i)
        {
            pixels[i].r = 255;
            pixels[i].g = 0;
            pixels[i].b = 0;
        }
    }
}


void UseFillConstructor()
{
    TestTimer t("UseFillConstructor");

    for(int i = 0; i < 1000; ++i)
    {
        int dimension = 999;

        std::vector<Pixel> pixels(dimension*dimension, Pixel(255,0,0));
    }
}

সুতরাং সরবরাহিত অতিরিক্ত "মান" পারফরম্যান্সকে অনেকটা ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে নামিয়েছে। কিন্তু ...

কম্পাইল:

gcc-6.2.0/bin/g++ -std=c++14 -O vector.cpp

আউটপুট:

UseArray completed in 1.02464 seconds
UseVector completed in 1.31056 seconds
UseConstructor completed in 1.47413 seconds
UseFillConstructor completed in 1.01555 seconds
UseVectorPushBack completed in 6.9597 seconds
The whole thing completed in 11.7851 seconds

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


0

এটি সংকলক পতাকার উপর নির্ভর করে বলে মনে হচ্ছে। এখানে একটি মানদণ্ডের কোড রয়েছে:

#include <chrono>
#include <cmath>
#include <ctime>
#include <iostream>
#include <vector>


int main(){

    int size = 1000000; // reduce this number in case your program crashes
    int L = 10;

    std::cout << "size=" << size << " L=" << L << std::endl;
    {
        srand( time(0) );
        double * data = new double[size];
        double result = 0.;
        std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
        for( int l = 0; l < L; l++ ) {
            for( int i = 0; i < size; i++ ) data[i] = rand() % 100;
            for( int i = 0; i < size; i++ ) result += data[i] * data[i];
        }
        std::chrono::steady_clock::time_point end   = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
        std::cout << "Calculation result is " << sqrt(result) << "\n";
        std::cout << "Duration of C style heap array:    " << duration << "ms\n";
        delete data;
    }

    {
        srand( 1 + time(0) );
        double data[size]; // technically, non-compliant with C++ standard.
        double result = 0.;
        std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
        for( int l = 0; l < L; l++ ) {
            for( int i = 0; i < size; i++ ) data[i] = rand() % 100;
            for( int i = 0; i < size; i++ ) result += data[i] * data[i];
        }
        std::chrono::steady_clock::time_point end   = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
        std::cout << "Calculation result is " << sqrt(result) << "\n";
        std::cout << "Duration of C99 style stack array: " << duration << "ms\n";
    }

    {
        srand( 2 + time(0) );
        std::vector<double> data( size );
        double result = 0.;
        std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
        for( int l = 0; l < L; l++ ) {
            for( int i = 0; i < size; i++ ) data[i] = rand() % 100;
            for( int i = 0; i < size; i++ ) result += data[i] * data[i];
        }
        std::chrono::steady_clock::time_point end   = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
        std::cout << "Calculation result is " << sqrt(result) << "\n";
        std::cout << "Duration of std::vector array:     " << duration << "ms\n";
    }

    return 0;
}

বিভিন্ন অপ্টিমাইজেশন পতাকা বিভিন্ন উত্তর দেয়:

$ g++ -O0 benchmark.cpp 
$ ./a.out 
size=1000000 L=10
Calculation result is 181182
Duration of C style heap array:    118441ms
Calculation result is 181240
Duration of C99 style stack array: 104920ms
Calculation result is 181210
Duration of std::vector array:     124477ms
$g++ -O3 benchmark.cpp
$ ./a.out 
size=1000000 L=10
Calculation result is 181213
Duration of C style heap array:    107803ms
Calculation result is 181198
Duration of C99 style stack array: 87247ms
Calculation result is 181204
Duration of std::vector array:     89083ms
$ g++ -Ofast benchmark.cpp 
$ ./a.out 
size=1000000 L=10
Calculation result is 181164
Duration of C style heap array:    93530ms
Calculation result is 181179
Duration of C99 style stack array: 80620ms
Calculation result is 181191
Duration of std::vector array:     78830ms

আপনার সঠিক ফলাফলগুলি পরিবর্তিত হবে তবে এটি আমার মেশিনে বেশ সাধারণ।


0

আমার অভিজ্ঞতায়, কখনও কখনও, কখনও কখনও, এর vector<int>চেয়ে বহুগুণ ধীর হতে পারে int[]। একটি বিষয় মনে রাখবেন যে ভেক্টরগুলির ভেক্টরগুলি একেবারেই বিপরীত int[][]। যেহেতু উপাদানগুলি সম্ভবত স্মৃতিতে সামঞ্জস্যপূর্ণ নয়। এর অর্থ আপনি মূলটির ভিতরে বিভিন্ন ভেক্টরকে পুনরায় আকার দিতে পারেন, তবে সিপিইউ তেমন উপাদানগুলির পাশাপাশি ক্যাশে করতে সক্ষম নাও হতে পারে int[][]

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