খুব বড় অ্যারেতে এলোমেলো অ্যাক্সেসের জন্য কোনও অপ্টিমাইজেশন যখন 95% ক্ষেত্রে এর মান হয় 0 বা 1 হয়?


133

খুব বড় অ্যারেতে এলোমেলো অ্যাক্সেসের জন্য কি কোনও সম্ভাব্য অপ্টিমাইজেশন রয়েছে (আমি বর্তমানে ব্যবহার uint8_tকরছি এবং আমি কী আরও ভাল সে সম্পর্কে জিজ্ঞাসা করছি)

uint8_t MyArray[10000000];

যখন অ্যারেতে কোনও অবস্থানের মান হয়

  • 0 বা 1 সমস্ত ক্ষেত্রে 95% এর জন্য ,
  • 2 মধ্যে 4% ক্ষেত্রে,
  • মধ্যে 3 এবং 255 অন্যান্য 1% ক্ষেত্রে?

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

আমি জিজ্ঞাসা করছি যেহেতু এটি এত বড় অ্যারে (10 মেগাবাইট) পাওয়া খুব অকার্যকর বলে মনে হয় যখন 5% বাদে প্রায় সমস্ত মান হয় 0 বা 1 হয়। সুতরাং যখন অ্যারের সমস্ত মানের 95% হবে 8 বিটের পরিবর্তে কেবলমাত্র 1 বিটের প্রয়োজন হবে, এটি মেমরির ব্যবহারকে প্রায় এক প্রস্থের ক্রম দ্বারা হ্রাস করবে। এটির মতো মনে হয় যে আরও একটি মেমরি দক্ষ সমাধান থাকতে হবে যা এর জন্য প্রয়োজনীয় র‌্যাম ব্যান্ডউইথকে হ্রাস করবে এবং ফলস্বরূপ এলোমেলো অ্যাক্সেসের জন্য উল্লেখযোগ্যভাবে দ্রুততর হবে।


36
দুটি বিট (0/1 / হ্যাশ টেবিল দেখুন) এবং 1 এর চেয়ে বড় মানের জন্য একটি হ্যাশটেবল?
ব্যবহারকারী 253751

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

7
মূলত, আপনি যা সম্পর্কে জিজ্ঞাসা করছেন তাকে স্পারসিটি বলা হয় ।
মতিন উলহাক

5
আরও তথ্যের প্রয়োজন ... অ্যাক্সেস এলোমেলো কেন, এবং অ-শূন্য মানগুলি কী কোনও নমুনা অনুসরণ করে?
Ext3h

4
@ আইভিলনোটেক্সিস্ট আইডোনোটেক্সিস্ট একটি পূর্ববর্তী পদক্ষেপ ঠিক হবে, তবে অ্যারে এখনও সময়ে সময়ে সংশোধন করা উচিত, তাই পূর্ববর্তী পদক্ষেপটি খুব ব্যয়বহুল হওয়া উচিত নয়।
জনআল

উত্তর:


155

একটি সাধারণ সম্ভাবনা যা মনে আসে তা হ'ল সাধারণ ক্ষেত্রে 2 টি বিটের সংকীর্ণ অ্যারে রাখা হয় এবং মূল্য অনুসারে পৃথক 4 বাইট (মূল উপাদান সূচকের জন্য 24 বিট, প্রকৃত মানের জন্য 8 বিট, তাই (idx << 8) | value)) সাজানো অ্যারে রাখা হয় অন্য একটা.

আপনি যখন কোনও মান সন্ধান করেন, আপনি প্রথমে 2bpp অ্যারে (ও (1)) এ অনুসন্ধান করেন; যদি আপনি 0, 1 বা 2 খুঁজে পান তবে এটি আপনার পছন্দ মত মান; আপনি যদি 3 টি খুঁজে পান তবে এর অর্থ এটি আপনাকে সেকেন্ডারি অ্যারেতে সন্ধান করতে হবে। এখানে আপনি 8 (O (লগ (এন) দ্বারা একটি ছোট এন দিয়ে, যেমন এটি 1% হওয়া উচিত) দ্বারা বাম-স্থানান্তরিত আপনার আগ্রহের সূচকটি সন্ধান করতে বাইনারি অনুসন্ধান করবেন এবং 4- এর থেকে মানটি বের করবেন বাইট জিনিস।

std::vector<uint8_t> main_arr;
std::vector<uint32_t> sec_arr;

uint8_t lookup(unsigned idx) {
    // extract the 2 bits of our interest from the main array
    uint8_t v = (main_arr[idx>>2]>>(2*(idx&3)))&3;
    // usual (likely) case: value between 0 and 2
    if(v != 3) return v;
    // bad case: lookup the index<<8 in the secondary array
    // lower_bound finds the first >=, so we don't need to mask out the value
    auto ptr = std::lower_bound(sec_arr.begin(), sec_arr.end(), idx<<8);
#ifdef _DEBUG
    // some coherency checks
    if(ptr == sec_arr.end()) std::abort();
    if((*ptr >> 8) != idx) std::abort();
#endif
    // extract our 8-bit value from the 32 bit (index, value) thingie
    return (*ptr) & 0xff;
}

void populate(uint8_t *source, size_t size) {
    main_arr.clear(); sec_arr.clear();
    // size the main storage (round up)
    main_arr.resize((size+3)/4);
    for(size_t idx = 0; idx < size; ++idx) {
        uint8_t in = source[idx];
        uint8_t &target = main_arr[idx>>2];
        // if the input doesn't fit, cap to 3 and put in secondary storage
        if(in >= 3) {
            // top 24 bits: index; low 8 bit: value
            sec_arr.push_back((idx << 8) | in);
            in = 3;
        }
        // store in the target according to the position
        target |= in << ((idx & 3)*2);
    }
}

আপনার প্রস্তাবিত একটি অ্যারের জন্য, এটি প্রথম অ্যারের জন্য 10000000/4 = 2500000 বাইট, এবং দ্বিতীয় অ্যারের জন্য 10000000 * 1% * 4 বি = 400000 বাইট নিতে হবে; সুতরাং 2900000 বাইট, অর্থাত্ মূল অ্যারের এক তৃতীয়াংশেরও কম, এবং সর্বাধিক ব্যবহৃত অংশটি মেমরিতে একসাথে রাখা হয়, যা ক্যাশে দেওয়ার জন্য ভাল হওয়া উচিত (এটি এমনকি এল 3 ফিটও হতে পারে)।

আপনার যদি 24-বিটের বেশি ঠিকানা প্রয়োজন হয় তবে আপনাকে "গৌণ স্টোরেজ" টিপতে হবে; এটি প্রসারিত করার তুচ্ছ উপায় হ'ল সূচকটির শীর্ষ 8 টি বিটকে স্যুইচ করার জন্য 256 এলিমেন্ট পয়েন্টার অ্যারে রাখা এবং উপরে 24-বিট সূচিযুক্ত সাজানো অ্যারেতে ফরোয়ার্ড করা।


দ্রুত মানদণ্ড

#include <algorithm>
#include <vector>
#include <stdint.h>
#include <chrono>
#include <stdio.h>
#include <math.h>

using namespace std::chrono;

/// XorShift32 generator; extremely fast, 2^32-1 period, way better quality
/// than LCG but fail some test suites
struct XorShift32 {
    /// This stuff allows to use this class wherever a library function
    /// requires a UniformRandomBitGenerator (e.g. std::shuffle)
    typedef uint32_t result_type;
    static uint32_t min() { return 1; }
    static uint32_t max() { return uint32_t(-1); }

    /// PRNG state
    uint32_t y;

    /// Initializes with seed
    XorShift32(uint32_t seed = 0) : y(seed) {
        if(y == 0) y = 2463534242UL;
    }

    /// Returns a value in the range [1, 1<<32)
    uint32_t operator()() {
        y ^= (y<<13);
        y ^= (y>>17);
        y ^= (y<<15);
        return y;
    }

    /// Returns a value in the range [0, limit); this conforms to the RandomFunc
    /// requirements for std::random_shuffle
    uint32_t operator()(uint32_t limit) {
        return (*this)()%limit;
    }
};

struct mean_variance {
    double rmean = 0.;
    double rvariance = 0.;
    int count = 0;

    void operator()(double x) {
        ++count;
        double ormean = rmean;
        rmean     += (x-rmean)/count;
        rvariance += (x-ormean)*(x-rmean);
    }

    double mean()     const { return rmean; }
    double variance() const { return rvariance/(count-1); }
    double stddev()   const { return std::sqrt(variance()); }
};

std::vector<uint8_t> main_arr;
std::vector<uint32_t> sec_arr;

uint8_t lookup(unsigned idx) {
    // extract the 2 bits of our interest from the main array
    uint8_t v = (main_arr[idx>>2]>>(2*(idx&3)))&3;
    // usual (likely) case: value between 0 and 2
    if(v != 3) return v;
    // bad case: lookup the index<<8 in the secondary array
    // lower_bound finds the first >=, so we don't need to mask out the value
    auto ptr = std::lower_bound(sec_arr.begin(), sec_arr.end(), idx<<8);
#ifdef _DEBUG
    // some coherency checks
    if(ptr == sec_arr.end()) std::abort();
    if((*ptr >> 8) != idx) std::abort();
#endif
    // extract our 8-bit value from the 32 bit (index, value) thingie
    return (*ptr) & 0xff;
}

void populate(uint8_t *source, size_t size) {
    main_arr.clear(); sec_arr.clear();
    // size the main storage (round up)
    main_arr.resize((size+3)/4);
    for(size_t idx = 0; idx < size; ++idx) {
        uint8_t in = source[idx];
        uint8_t &target = main_arr[idx>>2];
        // if the input doesn't fit, cap to 3 and put in secondary storage
        if(in >= 3) {
            // top 24 bits: index; low 8 bit: value
            sec_arr.push_back((idx << 8) | in);
            in = 3;
        }
        // store in the target according to the position
        target |= in << ((idx & 3)*2);
    }
}

volatile unsigned out;

int main() {
    XorShift32 xs;
    std::vector<uint8_t> vec;
    int size = 10000000;
    for(int i = 0; i<size; ++i) {
        uint32_t v = xs();
        if(v < 1825361101)      v = 0; // 42.5%
        else if(v < 4080218931) v = 1; // 95.0%
        else if(v < 4252017623) v = 2; // 99.0%
        else {
            while((v & 0xff) < 3) v = xs();
        }
        vec.push_back(v);
    }
    populate(vec.data(), vec.size());
    mean_variance lk_t, arr_t;
    for(int i = 0; i<50; ++i) {
        {
            unsigned o = 0;
            auto beg = high_resolution_clock::now();
            for(int i = 0; i < size; ++i) {
                o += lookup(xs() % size);
            }
            out += o;
            int dur = (high_resolution_clock::now()-beg)/microseconds(1);
            fprintf(stderr, "lookup: %10d µs\n", dur);
            lk_t(dur);
        }
        {
            unsigned o = 0;
            auto beg = high_resolution_clock::now();
            for(int i = 0; i < size; ++i) {
                o += vec[xs() % size];
            }
            out += o;
            int dur = (high_resolution_clock::now()-beg)/microseconds(1);
            fprintf(stderr, "array:  %10d µs\n", dur);
            arr_t(dur);
        }
    }

    fprintf(stderr, " lookup |   ±  |  array  |   ±  | speedup\n");
    printf("%7.0f | %4.0f | %7.0f | %4.0f | %0.2f\n",
            lk_t.mean(), lk_t.stddev(),
            arr_t.mean(), arr_t.stddev(),
            arr_t.mean()/lk_t.mean());
    return 0;
}

(কোড এবং ডেটা সর্বদা আমার বিটবকেটে আপডেট হয়)

উপরের কোডটি তাদের পোস্টে উল্লিখিত ওপি হিসাবে বিতরণ করা এলোমেলো ডেটা সহ 10 এম এলিমেন্ট অ্যারেকে পপুলেট করে, আমার ডেটা কাঠামো সূচনা করে এবং তারপরে:

  • আমার ডেটা স্ট্রাকচার সহ 10 এম উপাদানগুলির এলোমেলোভাবে অনুসন্ধান করে
  • মূল অ্যারের মাধ্যমে একই কাজ করে।

(লক্ষ করুন যে ক্রমবর্ধমান সন্ধানের ক্ষেত্রে অ্যারে সর্বদা বিশাল পরিমাপের সাথে জয়ী হয়, কারণ এটি আপনি করতে পারেন এমন সবচেয়ে ক্যাশে-বান্ধব অনুসন্ধান)

এই শেষ দুটি ব্লক 50 বার পুনরাবৃত্তি এবং সময়সীমা; শেষে, স্পিডআপের সাথে (লুকিং_মিয়ান / অ্যারে_মিয়ান) পাশাপাশি প্রতিটি ধরণের অনুসন্ধানের গড় এবং মান বিচ্যুতি গণনা করা এবং মুদ্রণ করা হয়।

আমি উপরের কোডটি -O3 -staticউবুন্টু 16.04 এ জি ++ 5.4.0 ( আরও কিছু সতর্কতা) দিয়ে সংকলন করেছি এবং এটি কয়েকটি মেশিনে চালিয়েছি ; তাদের বেশিরভাগ উবুন্টু 16.04 চলছে, কিছু পুরানো লিনাক্স, কিছু নতুন লিনাক্স। আমি মনে করি না এই ক্ষেত্রে ওএসটি মোটেই প্রাসঙ্গিক হওয়া উচিত।

            CPU           |  cache   |  lookup s)   |     array s)  | speedup (x)
Xeon E5-1650 v3 @ 3.50GHz | 15360 KB |  60011 ±  3667 |   29313 ±  2137 | 0.49
Xeon E5-2697 v3 @ 2.60GHz | 35840 KB |  66571 ±  7477 |   33197 ±  3619 | 0.50
Celeron G1610T  @ 2.30GHz |  2048 KB | 172090 ±   629 |  162328 ±   326 | 0.94
Core i3-3220T   @ 2.80GHz |  3072 KB | 111025 ±  5507 |  114415 ±  2528 | 1.03
Core i5-7200U   @ 2.50GHz |  3072 KB |  92447 ±  1494 |   95249 ±  1134 | 1.03
Xeon X3430      @ 2.40GHz |  8192 KB | 111303 ±   936 |  127647 ±  1503 | 1.15
Core i7 920     @ 2.67GHz |  8192 KB | 123161 ± 35113 |  156068 ± 45355 | 1.27
Xeon X5650      @ 2.67GHz | 12288 KB | 106015 ±  5364 |  140335 ±  6739 | 1.32
Core i7 870     @ 2.93GHz |  8192 KB |  77986 ±   429 |  106040 ±  1043 | 1.36
Core i7-6700    @ 3.40GHz |  8192 KB |  47854 ±   573 |   66893 ±  1367 | 1.40
Core i3-4150    @ 3.50GHz |  3072 KB |  76162 ±   983 |  113265 ±   239 | 1.49
Xeon X5650      @ 2.67GHz | 12288 KB | 101384 ±   796 |  152720 ±  2440 | 1.51
Core i7-3770T   @ 2.50GHz |  8192 KB |  69551 ±  1961 |  128929 ±  2631 | 1.85

ফলাফল ... মিশ্র!

  1. সাধারণভাবে, এই মেশিনগুলির বেশিরভাগ ক্ষেত্রেই একরকম স্পিডআপ থাকে, বা কমপক্ষে সেগুলি পার হয়।
  2. অ্যারে সত্যই "স্মার্ট স্ট্রাকচার" অনুসন্ধানকে ট্রাম্প করে এমন দুটি ক্ষেত্রে প্রচুর ক্যাশেযুক্ত মেশিনগুলিতে রয়েছে এবং বিশেষত ব্যস্ত নয়: উপরে Xeon E5-1650 (15 এমবি ক্যাশে) একটি নাইট বিল্ড মেশিন, এই মুহূর্তে বেশ অলস; Xeon E5-2697 (35 মেগাবাইট ক্যাশে) একটি নিষ্ক্রিয় মুহুর্তে উচ্চ পারফরম্যান্স গণনার জন্য একটি মেশিন। এটি অর্থবোধ করে না, মূল অ্যারেটি তাদের বিশাল ক্যাশে পুরোপুরি ফিট করে, সুতরাং কমপ্যাক্ট ডেটা স্ট্রাকচারটি কেবল জটিলতা যুক্ত করে।
  3. "পারফরম্যান্স বর্ণালী" এর বিপরীত দিকে - তবে যেখানে অ্যারেটি আরও দ্রুততর হয় সেখানে নম্র সেলেনরন রয়েছে যা আমার এনএএসকে শক্তি দেয়; এর এত ছোট ক্যাশে রয়েছে যে অ্যারে বা "স্মার্ট স্ট্রাকচার" এটিকে মোটেই ফিট করে না। যথেষ্ট ছোট ক্যাশেযুক্ত অন্যান্য মেশিনগুলি একইভাবে সম্পাদন করে।
  4. Xeon X5650 অবশ্যই কিছু সতর্কতার সাথে নেওয়া উচিত - এগুলি বেশ ব্যস্ত ডুয়াল-সকেট ভার্চুয়াল মেশিন সার্ভারে ভার্চুয়াল মেশিন; এটি বেশ ভাল হতে পারে, যদিও নামমাত্র এটির একটি শালীন পরিমাণের পরিমাণ থাকে, পরীক্ষার সময় এটি বেশিরভাগ সময় সম্পূর্ণ সম্পর্কযুক্ত ভার্চুয়াল মেশিন দ্বারা পচে যায়।

7
@ জনআল আপনার কোন কাঠামোর দরকার নেই। একটি ভাল uint32_tহবে। গৌণ বাফার থেকে কোনও উপাদান মুছলে তা অবশ্যই সাজানো ছেড়ে যায় leave কোনও উপাদান সন্নিবেশ করা std::lower_boundএবং তারপরে insert(পুরো জিনিসটি সংযোজন এবং পুনরায় সাজানোর চেয়ে) করা যায়। আপডেটগুলি পূর্ণ আকারের মাধ্যমিক অ্যারেটিকে আরও আকর্ষণীয় করে তোলে - আমি অবশ্যই এটি দিয়ে শুরু করব।
মার্টিন বোনার

6
@ জোহানআল যেহেতু মানটি হ'ল (idx << 8) + valআপনাকে মূল্য অংশটি নিয়ে চিন্তা করতে হবে না - কেবল একটি সরাসরি তুলনা ব্যবহার করুন। এটি সর্বদা কম ((idx+1) << 8) + valএবং এর চেয়ে কম তুলনা করবে((idx-1) << 8) + val
মার্টিন বোনার

3
@ জনল: যদি এটি কার্যকর হতে পারে তবে আমি একটি populateফাংশন যুক্ত করেছি যা জনবহুল হওয়া উচিত main_arrএবং প্রত্যাশিত sec_arrবিন্যাস অনুসারে lookup। আমি আসলে এটি চেষ্টা করে দেখিনি, তাই এটি সত্যিই সঠিকভাবে কাজ করবে বলে আশা করবেন না :-); যাইহোক, এটি আপনার সাধারণ ধারণা দেওয়া উচিত।
মাত্তেও ইটালিয়া

6
আমি এটি +1 দিচ্ছি মাত্র বেঞ্চমার্কিংয়ের জন্য। দক্ষতা সম্পর্কে এবং একাধিক প্রসেসরের ধরণের ফলাফলের জন্য একটি প্রশ্ন দেখতে ভাল লাগছে! নিস!
জ্যাক এইডলি

2
@ জোহনএইআই আপনার প্রকৃত ব্যবহারের ক্ষেত্রে এটির প্রোফাইল দেওয়া উচিত এবং অন্য কিছুই নয়। সাদা ঘরের গতি কিছু যায় আসে না।
জ্যাক এইডলি

33

অন্য বিকল্প হতে পারে

  • ফলাফল 0, 1 বা 2 হয় কিনা তা পরীক্ষা করে দেখুন
  • যদি না হয় নিয়মিত অনুসন্ধান

অন্য কথায় কিছু:

unsigned char lookup(int index) {
    int code = (bmap[index>>2]>>(2*(index&3)))&3;
    if (code != 3) return code;
    return full_array[index];
}

কোথায় bmap 3 টি মানের অর্থ সহ "বিট" সহ 2 বিট ব্যবহার করে।

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


4
আমি বলব যে এলোমেলো অ্যাক্সেসের সময়টিতে খুব বেশি ক্ষতি ছাড়াই, যতটা সম্ভব ক্যাশে হিট হওয়ার পক্ষে একটি ভাল সমঝোতা (হ্রাস কাঠামোটি সহজেই ক্যাশে ফিট করতে পারে)।
মেনেন্ডাল

আমি মনে করি এটি আরও উন্নত করা যেতে পারে। আমি অতীতে একই জাতীয় কিন্তু ভিন্ন সমস্যা নিয়ে সাফল্য পেয়েছি যেখানে শাখার ভবিষ্যদ্বাণী শোষণ অনেক সাহায্য করেছিল। এটা তোলে বিভক্ত করতে সাহায্য করতে পারে if(code != 3) return code;মধ্যেif(code == 0) return 0; if(code==1) return 1; if(code == 2) return 2;
kutschkem

@ কুত্স্কেম: সেক্ষেত্রে, __builtin_expect& সহ বা পিজিও সহায়তা করতে পারে।
মাত্তেও ইতালি

23

এটি একটি সুনির্দিষ্ট উত্তরের চেয়ে "দীর্ঘ মন্তব্য" বেশি

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

আপনার নিজের মতামতটি যেমন জানা যায় না তেমন কোনও পরিচিত নিদর্শনগুলির জন্য, কেউ সরাসরি যা ভাল তা জানতে পারবেন না এবং এটি বিশদগুলির উপর নির্ভর করে: কতটা এলোমেলোভাবে এলোমেলো অ্যাক্সেস হয় - এটি ডেটা আইটেমগুলির ক্লাস্টারগুলিতে অ্যাক্সেস করার সিস্টেম বা এটি সম্পূর্ণরূপে এলোমেলো মতো একটি অভিন্ন র্যান্ডম সংখ্যা জেনারেটর। টেবিলের ডেটাটি কি সম্পূর্ণ এলোমেলো, বা অন্যান্য মানগুলির বিক্ষিপ্তকরণের সাথে 0 এর পরে 1 এর ক্রম রয়েছে? আপনার যদি 0 এবং 1 এর যথাযথভাবে দীর্ঘ সিকোয়েন্স থাকে তবে রান দৈর্ঘ্যের এনকোডিংটি ভাল কাজ করবে, তবে আপনার "0/1 এর চেকবোর্ড" থাকলে কাজ করবে না। এছাড়াও, আপনাকে "শুরুর পয়েন্ট" এর একটি সারণী রাখতে হবে, যাতে আপনি যুক্তিসঙ্গতভাবে প্রাসঙ্গিক স্থানে কাজ করতে পারেন।

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

অনেক ক্ষেত্রে, "গতি এবং ছোট আকারের" মধ্যে সমঝোতা হ'ল সফটওয়্যার ইঞ্জিনিয়ারিংয়ের মধ্যে আপনাকে যে জিনিসগুলি বেছে নিতে হবে তার মধ্যে একটি [অন্যান্য প্রকৌশলতে এটি কোনও আপস করার দরকার নেই)। সুতরাং, "সহজ কোডের জন্য মেমরি নষ্ট করা" প্রায়শই পছন্দের পছন্দ। এই অর্থে, "সরল" সমাধানটি গতির পক্ষে সম্ভবত বেশ ভাল, তবে যদি আপনি র‌্যামের জন্য "আরও ভাল" ব্যবহার করেন, তবে টেবিলের আকারের জন্য অনুকূলিতকরণ আপনাকে পর্যাপ্ত কর্মক্ষমতা এবং আকারে একটি ভাল উন্নতি দিতে পারে। আপনি এটি অর্জন করতে পারেন এমন অনেকগুলি উপায় রয়েছে - যেমনটি একটি মন্তব্যে প্রস্তাবিত, একটি 2 বিট ক্ষেত্র যেখানে দুটি বা তিনটি সর্বাধিক সাধারণ মান সংরক্ষণ করা হয় এবং তারপরে অন্যান্য মানগুলির জন্য কিছু বিকল্প ডেটা ফর্ম্যাট - হ্যাশ-টেবিলটি আমার হবে প্রথম যোগাযোগ, কিন্তু একটি তালিকা বা বাইনারি গাছ খুব কার্যকর হতে পারে - আবার, এটি আপনার "0, 1 বা 2" নয় এমন ধরণের উপর নির্ভর করে। আবার, এটি নির্ভর করে যে কীভাবে মানগুলি টেবিলের মধ্যে "ছড়িয়ে ছিটিয়ে" থাকে - সেগুলি ক্লাস্টারে থাকে বা তারা আরও সমানভাবে বিতরণ করা প্যাটার্নের বেশি?

তবে এর সাথে একটি সমস্যা হ'ল আপনি এখনও র‌্যাম থেকে ডেটা পড়ছেন। এরপরে আপনি "এটি কোনও সাধারণ মূল্য নয়" সাথে মানিয়ে নেওয়ার জন্য কয়েকটি কোড সহ ডেটা প্রসেসিংয়ে আরও বেশি পরিমাণে ব্যয় করছেন।

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

শেষ পর্যন্ত, আপনাকে সম্ভবত পরীক্ষার জন্য মন্তব্য / উত্তরে একটি বা কয়েকটি ধারণাগুলি বাস্তবায়ন করতে হবে, দেখুন এটি আপনার সমস্যা সমাধানে সহায়তা করে কিনা, বা মেমরি বাস এখনও মূল সীমাবদ্ধ ফ্যাক্টর is


ধন্যবাদ! শেষ পর্যন্ত, আমি কেবলমাত্র দ্রুততর বিষয়ে আগ্রহী যখন 100% সিপিইউ এ জাতীয় অ্যারেগুলি (বিভিন্ন অ্যারেগুলির উপরে বিভিন্ন থ্রেড) লুপিংয়ে ব্যস্ত থাকে। বর্তমানে, একটি uint8_tঅ্যারে সহ, একই সময়ে (কোয়াড চ্যানেল সিস্টেমে) ~ 5 থ্রেড কাজ করার পরে র‌্যাম ব্যান্ডউইথ স্যাচুরেটেড হয়, সুতরাং 5 টির বেশি থ্রেড ব্যবহার করা আর কোনও সুবিধা দেয় না। আমি এটি> র‌্যাম ব্যান্ডউইথ ইস্যুগুলিতে চালিত না করে> 10 টি থ্রেড ব্যবহার করতে চাই তবে সিপিইউয়ের অ্যাক্সেসটি এত ধীরে হয়ে যায় যে 10 টি থ্রেড আগে 5 টি থ্রেডের চেয়ে কম হয়ে যায়, এটি অবশ্যই অগ্রগতি হতে পারে না।
জনআল

@ জনআল আপনার কাছে কয়টি কোর রয়েছে? আপনি যদি সিপিইউ আবদ্ধ হন তবে কোরের চেয়ে বেশি থ্রেড থাকার কোনও মানে নেই। এছাড়াও, জিপিইউ প্রোগ্রামিং দেখার জন্য সময় থাকতে পারে?
মার্টিন বোনার

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

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

13

আমি অতীতে যা করেছি তা হ'ল সামনে হ্যাশম্যাপ ব্যবহার করা হ'ল বিটসেটের হ্যাশম্যাপ ব্যবহার করা।

এটি মাত্তিওর উত্তরের তুলনায় স্থান অর্ধেক করেছে, তবে "ব্যতিক্রম" লুক্কুলগুলি ধীর হলে (যেমন অনেকগুলি ব্যতিক্রম রয়েছে) ধীর হতে পারে।

তবে প্রায়শই, "ক্যাশে রাজা"।


2
মাত্তিওর উত্তরের তুলনায় হ্যাশম্যাপের স্থানটি ঠিক কীভাবে অর্ধেক হবে ? এই হ্যাশম্যাপে কী হওয়া উচিত?
জনআল

1
@ জনল একটি 2-বিট বিটवेকের পরিবর্তে 1-বিট বিটসেট = বিটভেক ব্যবহার করছেন।
o11c

2
@ o11c আমি নিশ্চিত না আমি এটি সঠিকভাবে বুঝতে পেরেছি কিনা। আপনি যেখানে 1 বিট মান একটি অ্যারে আছে চাওয়ার কথা বলছেন 0মানে তাকানmain_arr এবং 1অর্থ তাকানsec_arr (Matteos কোডের ক্ষেত্রে)? এর জন্য অতিরিক্ত অতিরিক্ত ব্যবস্থার প্রয়োজন মাত্তোয়াস জবাবের চেয়ে বেশি, কারণ এটির একটি অতিরিক্ত অ্যারে। আমি পুরোটা বুঝতে পারছি না যে আপনি এটি ম্যাটিওসের উত্তরের তুলনায় কেবল অর্ধেক জায়গা ব্যবহার করে কীভাবে করবেন।
জনআল

1
আপনি এই পরিষ্কার করতে পারেন? আপনি প্রথমে এক্সপ্রেশনাল কেস গুলো দেখুন এবং তারপরে বিটম্যাপে দেখুন? যদি তা হয় তবে আমার সন্দেহ হয় যে হ্যাশটির ধীর গতিপথ বিটম্যাপের আকার হ্রাস করে সঞ্চয়কে ছাপিয়ে যাবে।
মার্টিন বোনার

আমি ভেবেছিলাম এটিকে হ্যাশলিংক বলা হয়েছিল - তবে গুগল কোনও প্রাসঙ্গিক হিট আপ করে না তাই এটি অন্যরকম কিছু হতে হবে। এটি সাধারণত যেভাবে কাজ করেছিল তা হ'ল একটি বাইট অ্যারে বলা যা 0_ 254 এর মধ্যে বলে যে বিস্তৃত সংখ্যাটি ছিল তার মানকে ধরে রাখবে। তারপরে আপনি 255 টি পতাকা হিসাবে ব্যবহার করবেন এবং যদি আপনার কোনও 255 উপাদান থাকে তবে আপনি কোনও সম্পর্কিত হ্যাশ টেবিলের মধ্যে সঠিক মানটি দেখতে চাইবেন। কেউ কি এটি বলা হয়েছিল মনে করতে পারেন? (আমি মনে করি আমি এটি সম্পর্কে পুরানো আইবিএম টিআর তে পড়েছি।) যাইহোক, আপনি @ o11c এর পরামর্শ অনুসারে এটিও সাজিয়ে রাখতে পারেন - সর্বদা হ্যাশটিতে প্রথমে খোঁজ করা, যদি এটি না থাকে তবে আপনার বিট অ্যারেটি দেখুন।
ডেভিডবাক

11

আপনার ডেটাতে প্যাটার্ন না থাকলে এটি কোনও সম্ভাবনাময় গতি বা আকারের অপ্টিমাইজেশনের সম্ভাবনা নেই এবং - ধরে নিচ্ছেন যে আপনি একটি সাধারণ কম্পিউটারকে টার্গেট করছেন - 10 এমবি যাইহোক এটি এত বড় ব্যাপার নয়।

আপনার প্রশ্নে দুটি অনুমান রয়েছে:

  1. ডেটাটি খুব খারাপভাবে সংরক্ষণ করা হচ্ছে কারণ আপনি সমস্ত বিট ব্যবহার করছেন না
  2. এটি আরও ভাল সঞ্চয় করা জিনিসগুলিকে আরও দ্রুত করে তুলবে।

আমি মনে করি এই দুটি অনুমানই ভুল are বেশিরভাগ ক্ষেত্রে ডেটা সঞ্চয় করার উপযুক্ত উপায় হ'ল সর্বাধিক প্রাকৃতিক উপস্থাপনা সঞ্চয় করা। আপনার ক্ষেত্রে, এটি আপনার জন্য গিয়েছিল: 0 এবং 255 এর মধ্যে একটি সংখ্যার জন্য বাইট other অন্য কোনও উপস্থাপনা আরও জটিল হবে এবং তাই - অন্যান্য সমস্ত জিনিস সমান - ধীর এবং ত্রুটির প্রবণ। এই সাধারণ নীতিটি থেকে সরে যাওয়ার জন্য আপনার 95% ডেটাতে সম্ভাব্য ছয় "নষ্ট" বিটের চেয়ে শক্তিশালী কারণ প্রয়োজন।

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


8

যদি ডেটা এবং অ্যাক্সেসগুলি এলোমেলোভাবে বিতরণ করা হয় তবে কার্য সম্পাদন সম্ভবত নির্ভর করে যে অ্যাক্সেসগুলির ভগ্নাংশটি বাইরের স্তরের ক্যাশে মিস এড়াতে পারে। অপ্টিমাইজ করার জন্য কী আকারে অ্যারে নির্ভরযোগ্যভাবে ক্যাশে সামঞ্জস্য করা যায় তা জেনে রাখা দরকার। যদি আপনার ক্যাশে প্রতি পাঁচটি কক্ষের জন্য একটি বাইট সমন্বিত করার পক্ষে যথেষ্ট বড় হয়, তবে সহজ পদ্ধতির মধ্যে একটি বাইটটি পাঁচটি বেস-থ্রি এনকোডেড মানকে ২-২ পরিসরে ধারণ করতে পারে (5 টি মানের 243 সংমিশ্রণ রয়েছে, তাই এটি হবে) বাইটে ফিট করুন) সহ 10,000,000 বাইট অ্যারের সাথে যখনই বেস -3 মান "2" নির্দেশ করে তখনই জিজ্ঞাসা করা হবে।

যদি ক্যাশেটি বড় না হয় তবে প্রতি 8 কোষে একটি বাইট সমন্বিত করতে পারে তবে আটটি বেস -3 মানগুলির 6,561 টি সম্ভাব্য সংমিশ্রণগুলির মধ্যে থেকে বেছে নিতে একটি বাইট মান ব্যবহার করা সম্ভব হবে না, তবে কেবলমাত্র এর প্রভাব 0 বা 1 কে 2 এ পরিবর্তন করা অন্যথায়-অপ্রয়োজনীয় অনুসন্ধানের কারণ হতে পারে, সঠিকতার জন্য সমস্ত 6,561 সমর্থন করার প্রয়োজন হয় না। পরিবর্তে, কেউ 256 সর্বাধিক "দরকারী" মানগুলিতে ফোকাস করতে পারে।

বিশেষত যদি 0 টি 1 এর চেয়ে বেশি সাধারণ হয় বা তদ্বিপরীত হয়, তবে 0 এবং 1 এর সংমিশ্রণগুলি 5 বা এর চেয়ে কম 1 এর সংশ্লেষ করতে 217 টি মান ব্যবহার করা উচিত, xxxx1111 এর মাধ্যমে xxxx0000 এনকোড করতে 16 মান, 0000xxxx এর মাধ্যমে এনকোড করা 16 1111XXXX এবং এক্সএক্সএক্সএক্সএক্সএক্সএক্সএক্সএক্স চারটি মান অন্য যে কোনও ব্যবহারের সন্ধান পেতে পারে। যদি বর্ণিত হিসাবে ডেটা এলোমেলোভাবে বিতরণ করা হয় তবে সমস্ত প্রশ্নের সামান্য সংখ্যাগুরু বাইটগুলিতে আঘাত করবে যা কেবল জিরো এবংগুলি রয়েছে (আটজনের সমস্ত দলের প্রায় ২/৩ অংশে, সমস্ত বিটগুলি শূন্য এবং বেশী হবে এবং এর প্রায় /// তাদের ছয় বা তার চেয়ে কম 1 বিট হবে); চারটি এক্স এর একটি বাইটে অবতরণ করবে না তাদের বেশিরভাগেরই শূন্য বা একটিতে অবতরণের 50% সম্ভাবনা থাকবে। সুতরাং, চারটি প্রশ্নের মধ্যে কেবল একটির জন্য বড়-অ্যারে অনুসন্ধান প্রয়োজন।

যদি ডেটা এলোমেলোভাবে বিতরণ করা হয় তবে ক্যাশে আটটি উপাদান অনুসারে একটি বাইট হ্যান্ডেল করার পক্ষে যথেষ্ট বড় না হয় তবে আটটি আইটেমের বেশি হ্যান্ডলিং করে প্রতিটি বাইটের সাহায্যে এই পদ্ধতির ব্যবহারের চেষ্টা করা যেতে পারে, তবে 0 বা 1 এর দিকে দৃ strong় পক্ষপাত না হলে , বড় অ্যারেতে একটি অনুসন্ধান না করেই পরিচালিত হওয়া মানগুলির ভগ্নাংশটি প্রতিটি বাইট দ্বারা পরিচালিত সংখ্যাটি বাড়ার সাথে সাথে সঙ্কুচিত হবে।


7

আমি @ o11c এর উত্তর যুক্ত করব, কারণ তাঁর কথাটি কিছুটা বিভ্রান্তিকর হতে পারে। আমার যদি শেষ বিট এবং সিপিইউ চক্রটি গ্রাহ্য করতে হয় তবে আমি নিম্নলিখিতটি করব।

আমরা 5% "অন্য কিছু" কেসযুক্ত একটি ভারসাম্য বাইনারি অনুসন্ধান গাছ তৈরি করে শুরু করব । প্রতিটি দেখার জন্য, আপনি দ্রুত গাছটি হাঁটাচ্ছেন: আপনার 10000000 উপাদান রয়েছে: 5% গাছের মধ্যে রয়েছে: সুতরাং গাছের ডেটা কাঠামোতে 500000 উপাদান রয়েছে। ও (লগ (এন)) সময়ে এটি হাঁটা আপনাকে 19 টি পুনরাবৃত্তি দেয়। আমি এতে কোনও বিশেষজ্ঞ নই, তবে আমি অনুমান করি যে এখানে কিছু মেমরি-দক্ষ বাস্তবায়ন রয়েছে। অনুমান করা যাক:

  • ভারসাম্যযুক্ত বৃক্ষ, সুতরাং সাবট্রি অবস্থান গণনা করা যেতে পারে (সূচকগুলি গাছের নোডগুলিতে সংরক্ষণ করার প্রয়োজন হয় না)। একইভাবে একটি হিপ (ডেটা স্ট্রাকচার) রৈখিক স্মৃতিতে সঞ্চয় করা হয়।
  • 1 বাইট মান (2 থেকে 255)
  • সূচকের জন্য 3 বাইট (10000000 23 বিট নেয়, যা 3 বাইট ফিট করে)

মোট, 4 বাইট: 500000 * 4 = 1953 কেবি। ক্যাশে ফিট!

অন্যান্য সমস্ত ক্ষেত্রে (0 বা 1), আপনি একটি বিট وেক্টর ব্যবহার করতে পারেন। মনে রাখবেন যে এলোমেলো অ্যাক্সেসের জন্য আপনি অন্যান্য 5% কেস ছেড়ে দিতে পারবেন না: 1.19 মেগাবাইট।

এই দুটির সংমিশ্রণটি প্রায় 3,099 এমবি ব্যবহার করে। এই কৌশলটি ব্যবহার করে, আপনি মেমরির একটি ফ্যাক্টর 3.08 সংরক্ষণ করতে পারবেন।

যাইহোক, এটি @ মাত্তিও ইটালিয়া উত্তর জবাব দেয় না (যা মেগাবাইট ব্যবহার করে) , এটি একটি করুণা। আমরা অতিরিক্ত কিছু করতে পারি এমন কি কিছু আছে? সর্বাধিক স্মৃতি গ্রাসকারী অংশটি গাছের সূচক 3 বাইট। যদি আমরা এটি 2 এ নামতে পারি তবে আমরা 488 কেবি সাশ্রয় করব এবং মোট মেমরির ব্যবহার হবে: 2.622 এমবি, যা কম!

আমরা এটা কিভাবে করব? আমাদের সূচিটি 2 বাইটে হ্রাস করতে হবে। আবার, 10000000 23 বিট নেয় আমাদের 7 টি বিট ফেলে দিতে সক্ষম হওয়া দরকার। আমরা সহজেই এটি করতে পারি 10000000 উপাদানগুলির পরিসীমা 2 ^ 7 (= 128) 78৮১২৫ উপাদানের অঞ্চলে বিভক্ত করে। এখন আমরা এই অঞ্চলের প্রতিটির জন্য গড়ে 3906 টি উপাদান দিয়ে একটি ভারসাম্য গাছ তৈরি করতে পারি। ডান গাছটি বাছাই করা লক্ষ্য সূচকের একটি সাধারণ বিভাগ দ্বারা 2 ^ 7 (বা বিটশিফ্ট) দ্বারা করা হয়>> 7 ) দ্বারা করা হয়। এখন সংরক্ষণ করার জন্য প্রয়োজনীয় সূচকটি বাকি 16 বিট দ্বারা উপস্থাপন করা যেতে পারে। নোট করুন যে গাছের দৈর্ঘ্যের জন্য কিছু ওভারহেড রয়েছে যা সংরক্ষণ করা দরকার তবে এটি নগণ্য। এছাড়াও মনে রাখবেন যে এই বিভাজন প্রক্রিয়াটি গাছটিতে হাঁটতে প্রয়োজনীয় সংখ্যক পুনরাবৃত্তি হ্রাস করে, এটি এখন হ্রাস পেয়ে 7 টি পুনরাবৃত্তি কম হয়, কারণ আমরা 7 বিট ফেলেছি: কেবল 12 পুনরাবৃত্তি বাকি রয়েছে।

নোট করুন যে আপনি পরবর্তী 8 টি বিট কেটে দেওয়ার জন্য তাত্ত্বিকভাবে প্রক্রিয়াটি পুনরাবৃত্তি করতে পারেন, তবে এটির জন্য আপনাকে গড়ে ~ 305 উপাদান সহ 2 ^ 15 সুষম গাছ তৈরি করতে হবে। এর ফলে 2.143 মেগাবাইটে গাছটি হাঁটতে কেবল 4 টি পুনরাবৃত্তি রয়েছে, যা আমরা শুরু হওয়া 19 টি পুনরাবৃত্তির তুলনায় যথেষ্ট গতিপথ is

চূড়ান্ত উপসংহার হিসাবে: এটি ক্ষুদ্র মেমরির ব্যবহারের মাধ্যমে 2-বিট ভেক্টর কৌশলকে পরাজিত করে তবে এটি বাস্তবায়নের জন্য পুরো সংগ্রাম। তবে যদি এটি ক্যাশে ফিটিংয়ের মধ্যে পার্থক্য তৈরি করতে পারে তবে এটি চেষ্টা করার মতো হতে পারে।


1
সাহসী প্রচেষ্টা!
ডেভিডবাক

1
এটি ব্যবহার করে দেখুন: যেহেতু 4% কেসই 2 মান হয় ... ব্যতিক্রমী কেসগুলির একটি সেট তৈরি করুন (> 1)। সত্যিই ব্যতিক্রমী ক্ষেত্রে (> 2) বর্ণিত হিসাবে কিছুটা গাছ তৈরি করুন। যদি সেট এবং ট্রি উপস্থিত থাকে তবে গাছের মান ব্যবহার করুন; যদি সেটে উপস্থিত থাকে এবং গাছ না থেকে থাকে তবে মান 2 ব্যবহার করুন, অন্যথায় (সেট-এ উপস্থিত নেই) আপনার বিটवेেক্টারে লুকআপ। গাছের মধ্যে কেবলমাত্র 100000 উপাদান (বাইট) থাকবে। সেটটিতে 500000 উপাদান রয়েছে (তবে কোনও মান নেই)। এটির বর্ধিত ব্যয়ের ন্যায্যতা দেওয়ার সময় কি আকার হ্রাস করবে? (100% লুকআপ সেট এ দেখায়; 5%
লুপগুলিকেও

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

5

আপনি যদি কেবল পঠিত অপারেশন করেন তবে একক সূচকে মান সূচকের অন্তর্নিহিত না করাই ভাল।

উদাহরণ স্বরূপ:

[0, 15000] = 0
[15001, 15002] = 153
[15003, 26876] = 2
[25677, 31578] = 0
...

এটি একটি স্ট্রাক্ট দিয়ে করা যেতে পারে। আপনি যদি ওও পদ্ধতির পছন্দ করেন তবে আপনি এটির মতো একটি শ্রেণীর সংজ্ঞাও দিতে চাইতে পারেন।

class Interval{
  private:
    uint32_t start; // First element of interval
    uint32_t end; // Last element of interval
    uint8_t value; // Assigned value

  public:
    Interval(uint32_t start, uint32_t end, uint8_t value);
    bool isInInterval(uint32_t item); // Checks if item lies within interval
    uint8_t getValue(); // Returns the assigned value
}

এখন আপনাকে কেবল গর্তের তালিকার পুনরাবৃত্তি করতে হবে এবং আপনার সূচকটি তার মধ্যে একটির মধ্যে রয়েছে যা গড়ের চেয়ে কম মেমরির নিবিড় হতে পারে তবে সিপিইউ রিসোর্সের জন্য বেশি খরচ করতে পারে তা পরীক্ষা করতে হবে।

Interval intervals[INTERVAL_COUNT];
intervals[0] = Interval(0, 15000, 0);
intervals[1] = Interval(15001, 15002, 153);
intervals[2] = Interval(15003, 26876, 2);
intervals[3] = Interval(25677, 31578, 0);
...

uint8_t checkIntervals(uint32_t item)

    for(int i=0; i<INTERVAL_COUNT-1; i++)
    {
        if(intervals[i].isInInterval(item) == true)
        {
            return intervals[i].getValue();
        }
    }
    return DEFAULT_VALUE;
}

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

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


4
আকর্ষণীয় ধারণা (+1) তবে আমি কিছুটা সন্দেহবাদী যে 0 টির বেশি লম্বা রান এবং / অথবা 1 এর দীর্ঘ রান না থাকলে এটি ওভারহেডকে ন্যায়সঙ্গত করে তোলে। বাস্তবে আপনি ডেটাটির একটি দৈর্ঘ্যের এনকোডিং ব্যবহার করার পরামর্শ দিচ্ছেন। এটি কিছু পরিস্থিতিতে ভাল হতে পারে তবে সম্ভবত এই সমস্যাটির জন্য ভাল সাধারণ পদ্ধতির নয়।
জন কোলেম্যান

ঠিক। বিশেষত এলোমেলো অ্যাক্সেসের জন্য, এটি একটি সাধারণ অ্যারের চেয়ে প্রায় অবশ্যই ধীর বা unt8_tএমনকি যদি এর থেকে অনেক কম স্মৃতি লাগে।

4

অনেক দিন আগে, আমি কেবল মনে করতে পারি ...

বিশ্ববিদ্যালয়ে আমরা একটি রে ট্রেসার প্রোগ্রামকে ত্বরান্বিত করার জন্য একটি টাস্ক পেয়েছি, এটি বার বার অ্যালগরিদম দ্বারা বারবার অ্যারে থেকে পড়তে হবে। একটি বন্ধু আমাকে সর্বদা র্যাম-রিডগুলি ব্যবহার করতে বলেছিল যা 4 বাইটের গুণক। সুতরাং আমি [x1, y1, z1, x2, y2, z2, ..., xn, yn, zn] এর এক ধরণ থেকে [x1, y1, z1,0, x2, y2, z2 এর ধরণে অ্যারে পরিবর্তন করেছি , 0, ..., XN, yn, Zn, 0]। মানে আমি প্রতিটি 3 ডি সমন্বয়ের পরে একটি খালি ক্ষেত্র যুক্ত করি। কিছু পারফরম্যান্স পরীক্ষার পরে: এটি দ্রুত ছিল। এত দীর্ঘ গল্পের সংক্ষিপ্তসার: র‌্যাম থেকে আপনার অ্যারে থেকে 4 বাইটের একাধিক পড়ুন, এবং সম্ভবত ডান সূচনার অবস্থান থেকেও, তাই আপনি যেখানে অনুসন্ধান সূচকটি আছেন সেখানে একটি সামান্য ক্লাস্টার পড়েন এবং সিপিইউতে এই ছোট্ট ক্লাস্টার থেকে অনুসন্ধান সূচকটি পড়েন। (আপনার ক্ষেত্রে আপনার ফিল্ড ফিল্ডস প্রবেশের প্রয়োজন হবে না, তবে ধারণাটি পরিষ্কার হওয়া উচিত)

নতুন সিস্টেমে অন্যান্য গুণগুলিও মূল হতে পারে।

এটি আপনার ক্ষেত্রে কাজ করবে কিনা আমি জানি না, সুতরাং যদি এটি কাজ করে না: দুঃখিত। যদি এটি কাজ করে তবে আমি কিছু পরীক্ষার ফলাফল শুনে খুশি হব।

PS: ওহ এবং যদি কোনও অ্যাক্সেস প্যাটার্ন বা কাছাকাছি অ্যাক্সেসড সূচকগুলি থাকে তবে আপনি ক্যাশেড ক্লাস্টারটি পুনরায় ব্যবহার করতে পারেন।

পিপিএস: এটি হতে পারে যে একাধিক ফ্যাক্টরটি আরও বেশি 16 বাইট বা এর মতো কিছু ছিল, এটি অনেক আগেই ছিল, যা আমি ঠিক মনে করতে পারি।


আপনি সম্ভবত ক্যাসেলিনগুলি সম্পর্কে ভাবছেন যা সাধারণত 32 বা 64 বাইট হয় তবে অ্যাক্সেস এলোমেলো হওয়ায় এটি এখানে খুব বেশি সহায়তা করবে না।
অ্যাশ Shāţi '

3

এটি দেখে আপনি নিজের ডেটা বিভক্ত করতে পারেন, উদাহরণস্বরূপ:

  • একটি বিটসেট যা সূচকযুক্ত হয় এবং 0 টি মান উপস্থাপন করে (স্ট্যান্ড :: ভেক্টর এখানে কার্যকর হবে)
  • একটি বিটসেট যা সূচকযুক্ত হয় এবং মান 1 উপস্থাপন করে
  • এই মানটি উল্লেখ করে এমন সূচকগুলি সমেত 2 টি মানের জন্য একটি ভিজিটর :: ভেক্টর
  • অন্যান্য মানগুলির একটি মানচিত্র (বা স্ট্যান্ড :: ভেক্টর>)

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

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

পরিমাপ নিশ্চিত করুন!


1
বেশী / জিরোদের জন্য একটি বিটসেট। দু'জনের জন্য সূচকগুলির একটি সেট। এবং বিশ্রামের জন্য একটি বিচ্ছিন্ন সাহসী অ্যারে।
Red.Wave

এটি সংক্ষিপ্ত সংক্ষিপ্তসার
JVApen

ওপিকে শর্তাদি জানতে দিন, যাতে তিনি প্রত্যেকটির বিকল্প বাস্তবায়নের সন্ধান করতে পারেন।
Red.Wave

2

ম্যাটস যেমন তাঁর মন্তব্য-উত্তরে উল্লেখ করেছেন, আপনার কী ধরণের ডেটা রয়েছে তা নির্দিষ্টভাবে না জেনে (উদাহরণস্বরূপ, এখানে 0 এর দীর্ঘ রান এবং আরও কিছু) এবং আপনার অ্যাক্সেস প্যাটার্নটি কী দেখায় তা বলা শক্ত is যেমন ("এলোমেলো" এর অর্থ "পুরো জায়গা জুড়ে" বা "পুরোপুরি লিনিয়ার ফ্যাশনে কঠোরভাবে নয়" বা "প্রতিটি মান ঠিক একবার, কেবল এলোমেলোভাবে তৈরি করা" বা ...)।

এটি বলেছিল, দুটি মনে আসার প্রক্রিয়া রয়েছে:

  • বিট অ্যারে; অর্থাত্, যদি আপনার কেবল দুটি মান থাকে তবে আপনি 8 এর গুণক দ্বারা আপনার অ্যারেটিকে তুচ্ছভাবে সংকুচিত করতে পারেন; আপনার যদি 4 টি মান (বা "3 মানগুলি + সমস্ত কিছু") থাকে তবে আপনি দুটিটির একটি ফ্যাক্টর দ্বারা সংক্ষিপ্ত করতে পারেন। যা কেবল সমস্যার জন্য উপযুক্ত নয় এবং আপনার মানদণ্ডের প্রয়োজন হবে, বিশেষত যদি আপনার সত্যিকারের এলোমেলো অ্যাক্সেস প্যাটার্ন থাকে যা আপনার ক্যাশেগুলি ছেড়ে যায় এবং তাই অ্যাক্সেসের সময়টি একেবারেই পরিবর্তন না করে।
  • (index,value)বা (value,index)টেবিল। উদাহরণস্বরূপ, 1% কেসের জন্য একটি খুব ছোট টেবিল থাকতে পারে, 5% কেসের জন্য একটি টেবিল থাকতে পারে (যার জন্য কেবল সূচকগুলি সবগুলি একই মানের হিসাবে সঞ্চয় করতে হবে), এবং চূড়ান্ত দুটি ক্ষেত্রে বড় সংকীর্ণ বিট অ্যারে রয়েছে। এবং "টেবিল" দিয়ে আমি এমন কিছু বোঝাচ্ছি যা তুলনামূলকভাবে দ্রুত দেখার অনুমতি দেয়; উদাহরণস্বরূপ, আপনার কাছে যা পাওয়া যায় এবং আপনার প্রকৃত প্রয়োজনীয়তার উপর নির্ভর করে সম্ভবত একটি হ্যাশ, একটি বাইনারি গাছ এবং অন্যান্য। এই সাবটাবলগুলি যদি আপনার প্রথম / দ্বিতীয় স্তরের ক্যাশে ফিট করে তবে আপনি ভাগ্যবান হতে পারেন।

1

আমি সি এর সাথে খুব বেশি পরিচিত নই, তবে সি ++ এ আপনি 0 - 255 এর পরিসীমাতে কোনও পূর্ণসংখ্যার প্রতিনিধিত্ব করতে স্বাক্ষরবিহীন চরটি ব্যবহার করতে পারেন ।

সাধারণ ইনটের তুলনায় (আবার, আমি জাভা এবং সি ++ বিশ্ব থেকে আসছি ) যেখানে 4 বাইট (32 বিট) প্রয়োজন, একটি স্বাক্ষরবিহীন অক্ষরের জন্য 1 বাইট (8 বিট) প্রয়োজন। সুতরাং এটি অ্যারের মোট আকার 75% কমাতে পারে।


সম্ভবত এটির ক্ষেত্রে ইতিমধ্যে uint8_t - 8 এর অর্থ 8 বিট।
পিটার মর্টেনসেন

-4

আপনি আপনার অ্যারের সমস্ত বন্টন বৈশিষ্ট্য সংক্ষিপ্তভাবে বর্ণনা করেছেন; অ্যারে শিরসঁচালন

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

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


18
আমার সন্দেহ হয় যে অ্যাক্সেসগুলি অনাকাঙ্ক্ষিত, এটি আসলে এলোমেলো নয় তা বোঝাতে এখানে "এলোমেলো অ্যাক্সেস" ব্যবহার করা হয়েছিল। (অর্থাত্ এটি "এলোমেলো অ্যাক্সেস ফাইলগুলি" অর্থে তৈরি করা হয়েছে)
মাইকেল কে

হ্যাঁ, সম্ভবত ওপি অবশ্য পরিষ্কার নয়। যদি ওপির অ্যাক্সেসগুলি কোনওভাবে এলোমেলো না হয় তবে অন্যান্য উত্তর অনুসারে কিছু স্পারস অ্যারে নির্দেশিত হয়।
দথোমাস

1
আমি মনে করি আপনার সেখানে একটি বক্তব্য আছে, যেহেতু ওপি নির্দেশ করেছে যে তিনি একটি এলোমেলোভাবে ক্রমে পুরো অ্যারেটি লুপ করবেন। যে ক্ষেত্রে কেবল বিতরণগুলি পর্যবেক্ষণ করা দরকার, এটি একটি ভাল উত্তর।
ইনগো শ্যাচাল-শাপ্প 21
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.