কেননা বাছাই করা অ্যারে প্রক্রিয়াজাতকরণ একটি অরসেটেড অ্যারে প্রক্রিয়াজাতকরণের চেয়ে দ্রুত?


24438

এখানে সি ++ কোডের একটি অংশ যা কিছু খুব অদ্ভুত আচরণ দেখায়। কিছু অদ্ভুত কারণে, অলৌকিকভাবে ডেটা বাছাই করা কোডটি প্রায় ছয়গুণ দ্রুত করে তোলে:

#include <algorithm>
#include <ctime>
#include <iostream>

int main()
{
    // Generate data
    const unsigned arraySize = 32768;
    int data[arraySize];

    for (unsigned c = 0; c < arraySize; ++c)
        data[c] = std::rand() % 256;

    // !!! With this, the next loop runs faster.
    std::sort(data, data + arraySize);

    // Test
    clock_t start = clock();
    long long sum = 0;

    for (unsigned i = 0; i < 100000; ++i)
    {
        // Primary loop
        for (unsigned c = 0; c < arraySize; ++c)
        {
            if (data[c] >= 128)
                sum += data[c];
        }
    }

    double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

    std::cout << elapsedTime << std::endl;
    std::cout << "sum = " << sum << std::endl;
}
  • ছাড়াই std::sort(data, data + arraySize);কোডটি 11.54 সেকেন্ডে চলে।
  • সাজানো ডেটা সহ, কোডটি 1.93 সেকেন্ডে চলে।

প্রাথমিকভাবে, আমি ভেবেছিলাম এটি কেবল একটি ভাষা বা সংকলকটি বিসংগঠিত হতে পারে, তাই আমি জাভা চেষ্টা করেছিলাম:

import java.util.Arrays;
import java.util.Random;

public class Main
{
    public static void main(String[] args)
    {
        // Generate data
        int arraySize = 32768;
        int data[] = new int[arraySize];

        Random rnd = new Random(0);
        for (int c = 0; c < arraySize; ++c)
            data[c] = rnd.nextInt() % 256;

        // !!! With this, the next loop runs faster
        Arrays.sort(data);

        // Test
        long start = System.nanoTime();
        long sum = 0;

        for (int i = 0; i < 100000; ++i)
        {
            // Primary loop
            for (int c = 0; c < arraySize; ++c)
            {
                if (data[c] >= 128)
                    sum += data[c];
            }
        }

        System.out.println((System.nanoTime() - start) / 1000000000.0);
        System.out.println("sum = " + sum);
    }
}

অনুরূপ তবে কম চরম ফলাফল সহ।


আমার প্রথম চিন্তাটি ছিল যে বাছাই করা ডেটা ক্যাশে brings

  • কি হচ্ছে?
  • কেননা বাছাই করা অ্যারে প্রক্রিয়াজাতকরণ একটি অরসেটেড অ্যারে প্রক্রিয়াজাতকরণের চেয়ে দ্রুত?

কোডটি কিছু স্বতন্ত্র শর্তাদি সংক্ষিপ্ত করছে, যাতে আদেশের বিষয়টি বিবেচনা করা উচিত নয়।



15
@ সচিনভাইর্মা আমার মাথার উপরের অংশটি বন্ধ করুন: 1) জেভিএম শেষ অবধি শর্তযুক্ত পদক্ষেপ ব্যবহারের জন্য যথেষ্ট স্মার্ট হতে পারে। 2) কোডটি মেমরি-সীমাবদ্ধ। 200 মি সিপিইউ ক্যাশে ফিট করার জন্য অনেক বড়। সুতরাং পারফরম্যান্সটি ব্রাঞ্চ করার পরিবর্তে মেমরি ব্যান্ডউইথ দ্বারা বাধা হয়ে উঠবে।
রহস্যময়

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

14
@ সচিনভাইর্মা এটি করে, তবে অ্যারেটি যখন বড় হয় তখন একটি বড় ফ্যাক্টর সম্ভবত খেলতে আসে - মেমরি ব্যান্ডউইথ। স্মৃতি সমতল নয় । মেমরি অ্যাক্সেস খুব ধীর এবং ব্যান্ডউইথ একটি সীমিত পরিমাণে আছে। জিনিসগুলি অতি-সরলকরণের জন্য, কেবলমাত্র অনেকগুলি বাইট রয়েছে যা নির্দিষ্ট সময়ের মধ্যে সিপিইউ এবং মেমরির মধ্যে স্থানান্তরিত হতে পারে। এই প্রশ্নের মতো একটি সাধারণ কোড সম্ভবত ভুল সীমাবদ্ধতার দ্বারা ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ছড়িয়ে পড়বে limit এটি 32768 (128KB) এর অ্যারের সাথে ঘটে না কারণ এটি সিপিইউয়ের L2 ক্যাশে ফিট করে।
রহস্যময়

11
ব্রাঞ্চস্কোপ নামে একটি নতুন সুরক্ষা ত্রুটি রয়েছে: cs.ucr.edu/~nael/pubs/asplos18.pdf
Veve

উত্তর:


31781

আপনি শাখার পূর্বাভাস ব্যর্থতার শিকার ।


শাখার ভবিষ্যদ্বাণী কী?

একটি রেলপথ জংশন বিবেচনা করুন:

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

যুক্তির স্বার্থে, ধরুন এটি দীর্ঘ দূরত্ব বা রেডিও যোগাযোগের আগে - 1800 এর দশকে ফিরে এসেছে।

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

ট্রেনগুলি ভারী এবং প্রচুর জড়তা রয়েছে। তাই তারা শুরু করতে এবং ধীর হয়ে যেতে চিরকালের জন্য নেয়।

একটি ভাল উপায় আছে কি? আপনি অনুমান করুন ট্রেনটি কোন দিকে যাবে!

  • আপনি যদি সঠিক অনুমান করেন তবে এটি অবিরত থাকবে।
  • আপনি যদি ভুল অনুমান করে থাকেন তবে ক্যাপ্টেন থামবেন, ব্যাক আপ করবেন এবং স্যুইচটি সরিয়ে ফেলার জন্য আপনাকে চিৎকার করবেন। তারপরে এটি অন্য পথটি আবার চালু করতে পারে।

আপনি যদি প্রতিবার ঠিক মতো অনুমান করেন তবে ট্রেনটি কখনও থামতে হবে না।
আপনি যদি খুব ঘন ঘন ভুল অনুমান করেন, ট্রেন থামাতে, ব্যাক আপ করতে এবং পুনরায় চালু করতে প্রচুর সময় ব্যয় করবে।


একটি বিবৃতি বিবেচনা করুন: প্রসেসর স্তরে, এটি একটি শাখা নির্দেশ:

একটি বিবৃতি ধারণ করে সংকলিত কোডের স্ক্রিনশট

আপনি একটি প্রসেসর এবং আপনি একটি শাখা দেখতে। কোন পথে যাবে তা আপনার কোনও ধারণা নেই। আপনি কি করেন? আপনি কার্যকর করা বন্ধ করে দিন এবং পূর্ববর্তী নির্দেশাবলী সম্পূর্ণ না হওয়া পর্যন্ত অপেক্ষা করুন। তারপরে আপনি সঠিক পথে চালিয়ে যান।

আধুনিক প্রসেসরগুলি জটিল এবং দীর্ঘ পাইপলাইন রয়েছে। সুতরাং তারা "উষ্ণতা" এবং "ধীর গতিতে" চিরকাল নিয়ে যায়।

একটি ভাল উপায় আছে কি? আপনি অনুমান করেন যে শাখাটি কোন দিকে যাবে!

  • আপনি যদি সঠিক অনুমান করেন তবে আপনি চালিয়ে যান।
  • আপনি যদি ভুল অনুমান করেন তবে আপনাকে পাইপলাইনটি ফ্লাশ করে শাখায় ফিরে যেতে হবে। তারপরে আপনি অন্য পথটি আবার চালু করতে পারেন।

আপনি যদি প্রতিবার ঠিক মতো অনুমান করেন , মৃত্যুদণ্ড কার্যকর করা কখনই থামবে না।
যদি আপনি খুব ঘন ঘন ভুল অনুমান করেন তবে আপনি স্টলিং, পিছন ফিরে এবং পুনরায় চালু করতে প্রচুর সময় ব্যয় করেন।


এটি শাখার পূর্বাভাস। আমি স্বীকার করি এটি সর্বোত্তম উপমা নয় কারণ ট্রেনটি কেবল একটি পতাকা দিয়ে দিকটি নির্দেশ করতে পারে। তবে কম্পিউটারগুলিতে প্রসেসর জানে না কোন শাখাটি শেষ মুহুর্ত পর্যন্ত কোন দিকে যাবে।

সুতরাং আপনি কীভাবে কৌশলগতভাবে অনুমান করতে পারবেন যে ট্রেনটি ব্যাক আপ করতে এবং অন্যান্য পথে নামতে হবে তার সংখ্যা কত বার কমাতে হবে? আপনি অতীত ইতিহাস তাকান! যদি ট্রেনটি 99% সময়ের মধ্যে চলে যায় তবে আপনি অনুমান করেন যে বাম। যদি এটি বিকল্প হয়, তবে আপনি আপনার অনুমানগুলি বিকল্প করুন। যদি এটি প্রতি তিনবার একবারে যায়, আপনি একই অনুমান করুন ...

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

বেশিরভাগ অ্যাপ্লিকেশনগুলিতে ভাল আচরণ করা শাখা রয়েছে। সুতরাং আধুনিক শাখার ভবিষ্যদ্বাণীকারীরা সাধারণত 90% হিট রেট অর্জন করতে পারে। কিন্তু যখন কোন অননুমোদিত শাখাগুলির সাথে কোনও স্বীকৃতিযোগ্য নিদর্শন ছাড়াই सामना করা হয়, তখন শাখার ভবিষ্যদ্বাণীকারীগুলি কার্যত অকেজো।

আরও পঠন: উইকিপিডিয়াতে "ব্রাঞ্চের ভবিষ্যদ্বাণী" নিবন্ধ


উপর থেকে ইঙ্গিত হিসাবে, দোষী এই যদি বিবৃতি:

if (data[c] >= 128)
    sum += data[c];

লক্ষ্য করুন যে ডেটা 0 এবং 255 এর মধ্যে সমানভাবে বিতরণ করা হয়েছে the এর পরে, তারা সকলেই if-বিবৃতি প্রবেশ করবে।

এটি শাখার ভবিষ্যদ্বাণীকারীর পক্ষে খুব বন্ধুত্বপূর্ণ কারণ শাখাটি ক্রমাগত একই দিকে বহুবার চলে। এমনকি একটি সাধারণ স্যাচুরেটিং কাউন্টার দিকটি স্যুইচ করার পরে কয়েকটি পুনরাবৃত্তি বাদ দিয়ে শাখাকে সঠিকভাবে ভবিষ্যদ্বাণী করবে।

দ্রুত দৃশ্যায়ন:

T = branch taken
N = branch not taken

data[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...
branch = N  N  N  N  N  ...   N    N    T    T    T  ...   T    T    T  ...

       = NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT  (easy to predict)

যাইহোক, যখন ডেটা সম্পূর্ণরূপে এলোমেলো হয়, তখন শাখার ভবিষ্যদ্বাণীটি অকেজো হয়ে যায়, কারণ এটি এলোমেলো ডেটা পূর্বাভাস দিতে পারে না। সুতরাং সম্ভবত প্রায় 50% ভুল ধারণা করা হবে (এলোমেলো অনুমানের চেয়ে ভাল)।

data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118,  14, 150, 177, 182, 133, ...
branch =   T,   T,   N,   T,   T,   T,   T,  N,   T,   N,   N,   T,   T,   T,   N  ...

       = TTNTTTTNTNNTTTN ...   (completely random - hard to predict)

তাহলে কি করা উচিত?

যদি সংকলক শর্তাধীন পদক্ষেপে শাখাটিকে অনুকূল করতে সক্ষম না হয়, আপনি যদি পারফরম্যান্সের জন্য পাঠযোগ্যতার ত্যাগ করতে চান তবে আপনি কিছু হ্যাক চেষ্টা করতে পারেন।

প্রতিস্থাপন করুন:

if (data[c] >= 128)
    sum += data[c];

সঙ্গে:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

এটি শাখাটি অপসারণ করে এবং কিছু বিটওয়াইজ অপারেশন দিয়ে এটি প্রতিস্থাপন করে।

(দ্রষ্টব্য যে এই হ্যাকটি কঠোরভাবে যদি ইফ-স্টেটমেন্টের সাথে সমান হয় না তবে তবে এই ক্ষেত্রে এটি সমস্ত ইনপুট মানের জন্য বৈধ data[]))

বেঞ্চমার্কস: কোর আই 7920 @ 3.5 গিগাহার্টজ

সি ++ - ভিজ্যুয়াল স্টুডিও 2010 - এক্স 64 প্রকাশ

//  Branch - Random
seconds = 11.777

//  Branch - Sorted
seconds = 2.352

//  Branchless - Random
seconds = 2.564

//  Branchless - Sorted
seconds = 2.587

জাভা - নেটবিয়ানস 7.1.1 জেডিকে 7 - এক্স 64

//  Branch - Random
seconds = 10.93293813

//  Branch - Sorted
seconds = 5.643797077

//  Branchless - Random
seconds = 3.113581453

//  Branchless - Sorted
seconds = 3.186068823

পর্যবেক্ষণ:

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

থাম্বের একটি সাধারণ নিয়ম হ'ল সমালোচনামূলক লুপগুলিতে ডেটা-নির্ভর ব্রাঞ্চিং এড়ানো (যেমন এই উদাহরণ হিসাবে)।


হালনাগাদ:

  • এক্সস with৪ এর সাথে -O3বা এর সাথে জিসিসি 6..1.১ -ftree-vectorizeশর্তযুক্ত পদক্ষেপ উত্পন্ন করতে সক্ষম। সুতরাং বাছাই করা এবং অরসোর্ট করা ডেটার মধ্যে কোনও পার্থক্য নেই - উভয়ই দ্রুত।

    (বা কিছুটা দ্রুত: ইতিমধ্যে সাজানো কেসটির জন্য, ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে নামিয়ে আনতেcmov পারে যদি জিসিসি এটিকে ন্যায়বিচারের পরিবর্তে সমালোচনামূলক পথে ফেলে add, বিশেষত ব্রডওয়েলের আগে যেখানে cmov2 সাইকেল বিন্যাস রয়েছে : জিসিসি অপ্টিমাইজেশান পতাকা -O3 কোড -O2 এর চেয়ে ধীর করে তোলে )

  • ভিসি ++ ২০১০ এমনকি এই শাখার অধীনে থাকা শর্তাধীন শর্তাদি তৈরি করতে অক্ষম /Ox

  • ইন্টেল সি ++ কম্পাইলার (আইসিসি) 11 অলৌকিক কিছু করে। এটি দুটি লুপকে আন্তঃসংযোগ করে , এর ফলে বাইরের লুপে অবিশ্বাস্য শাখা উত্তোলন করা হয়। সুতরাং এটি শুধুমাত্র ভুল অনুমানের প্রতিরোধী নয়, এটি ভিসি ++ এবং জিসিসি যা কিছু উত্পন্ন করতে পারে তার দ্বিগুণও দ্রুত! অন্য কথায়, আইসিসি বেঞ্চমার্ককে পরাস্ত করতে টেস্ট-লুপের সুযোগ নিয়েছে ...

  • যদি আপনি ইন্টেলটি শাখাবিহীন কোডটি সংকলন করেন তবে এটি কেবল ডানদিকের সাথে এটি ভেক্টরাইজ করে ... এবং শাখার সাথে ঠিক তত দ্রুত (লুপ ইন্টারচেঞ্জের সাথে) is

এটি দেখাতে সক্ষম হয় যে এমনকি পরিপক্ক আধুনিক সংকলকরা কোড অপ্টিমাইজ করার ক্ষমতাকে বিভিন্নভাবে বদলে যেতে পারে ...


255
এই ফলোআপ প্রশ্নটি একবার দেখুন: স্ট্যাকওভারফ্লো / প্রশ্ন / ১১১২7676 29 1 / ৮ ইন্টেল সংকলক বাইরের লুপটি সম্পূর্ণরূপে মুক্তি পাওয়ার খুব কাছে এসেছিল।
রহস্যময়

23
@ মিস্টিয়াল ট্রেন / সংকলক কীভাবে জানতে পারে যে এটি ভুল পথে প্রবেশ করেছে?
onmyway133

25
@ ওব: শ্রেণিবদ্ধ মেমরি কাঠামো দেওয়া, ক্যাশে মিসের ব্যয় কী হবে তা বলা অসম্ভব। এটি L1 এ মিস হয়ে যায় এবং ধীর L2 এ সমাধান হতে পারে, বা L3 এ মিস হয়ে সিস্টেম মেমোরিতে সমাধান হতে পারে। যাইহোক, কিছু উদ্ভট কারণে যদি না এই ক্যাশে মিসটি অনাবাসিক পৃষ্ঠায় ডিস্ক থেকে লোড হওয়ার স্মৃতি তৈরি করে, আপনার একটি ভাল বক্তব্য ... মেমরিটি প্রায় 25-30 বছরের মধ্যে মিলিসেকেন্ডের সীমাতে অ্যাক্সেসের সময় পায় নি memory ;)
অ্যান্ডন এম কলম্যান

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

21
@ সন্দীপ হ্যাঁ প্রসেসরের এখনও শাখা পূর্বাভাস আছে। যদি কিছু পরিবর্তিত হয় তবে এটি সংকলকগুলি। আজকাল, আমি বাজি ধরেছি যে তারা আইসিসি এবং জিসিসি (আন্ডার -O3) এখানে যা করেছে তা সম্ভবত বেশি করার সম্ভাবনা রয়েছে - অর্থাৎ, শাখাটি সরান। এই প্রশ্নটি কতটা হাই প্রোফাইল, এটি দেওয়া খুব সম্ভব যে এই প্রশ্নে কেসটি হ্যান্ডেল করার জন্য সংকলকগণ আপডেট করা হয়েছে। অবশ্যই এসও মনোযোগ দিন। এবং এই প্রশ্নে এটি ঘটেছে যেখানে 3 সপ্তাহের মধ্যে জিসিসি আপডেট করা হয়েছিল। আমি এখানে দেখছি না কেন এটি এখানেও ঘটবে না।
রহস্যময়

4086

শাখার পূর্বাভাস।

বাছাই করা অ্যারে দিয়ে শর্তটি data[c] >= 128প্রথমে falseমানগুলির একটি রেখার জন্য, তারপরে trueপরবর্তী সমস্ত মানগুলির হয়ে ওঠে । এটি পূর্বাভাস দেওয়া সহজ। একটি অমীমাংসিত অ্যারে সহ, আপনি শাখা ব্যয়ের জন্য অর্থ প্রদান করুন।


105
শাখার পূর্বাভাসটি বিভিন্ন ধরণের সাথে সাজানো অ্যারে বনাম অ্যারেগুলিতে আরও ভাল কাজ করে? উদাহরণস্বরূপ, অ্যারের জন্য -> {10, 5, 20, 10, 40, 20, ... the প্যাটার্ন থেকে অ্যারের পরবর্তী উপাদানটি 80 হয় branch পরের উপাদানটি এখানে ৮০ হলে প্যাটার্নটি অনুসরণ করা হয়? অথবা এটি সাধারণত বাছাই করা অ্যারেগুলিতে সহায়তা করে?
অ্যাডাম ফ্রিম্যান

132
সুতরাং মূলত বিগ-ও সম্পর্কে আমি প্রচলিতভাবে শিখেছি সমস্ত কি উইন্ডোটি বাইরে? একটি শাখাগুলি ব্যয়ের চেয়ে বাছাইয়ের খরচ বহন করা ভাল?
Agrim পাঠক

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

65
শাখার পূর্বাভাস কখন ঘটে? ভাষা কখন জানতে পারবে যে অ্যারে বাছাই করা হয়েছে? আমি এমন অ্যারের পরিস্থিতি নিয়ে ভাবছি যা দেখতে দেখতে: [1,2,3,4,5, ... 998,999,1000, 3, 10001, 10002]? এই অস্পষ্ট 3 চলমান সময় বৃদ্ধি করবে? এটি কি অরসোর্টেড অ্যারে হিসাবে দীর্ঘ হবে?
ফিলিপ বার্টুজি

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

3310

ডেটা বাছাই করার সময় পারফরম্যান্সে মারাত্মক উন্নতি হওয়ার কারণ হ'ল মিস্টিয়ালের উত্তরে সুন্দরভাবে ব্যাখ্যা করা অনুসারে শাখার পূর্বাভাস জরিমানা সরানো হয়েছে ।

এখন, আমরা কোড তাকান যদি

if (data[c] >= 128)
    sum += data[c];

আমরা দেখতে পাচ্ছি যে if... else...যখন কোনও শর্তটি সন্তুষ্ট হয় তখন এই নির্দিষ্ট শাখার অর্থ হ'ল কিছু যুক্ত করা। এই ধরণের শাখাটি সহজেই শর্তসাপেক্ষে সরানো বিবৃতিতে রূপান্তরিত হতে পারে যা শর্তসাপেক্ষে সরানো নির্দেশে: cmovlএকটি x86সিস্টেমে সংকলিত হবে । শাখা এবং এইভাবে সম্ভাব্য শাখার পূর্বাভাস জরিমানা সরানো হবে।

ইন C, সুতরাং C++, বিবৃতিটি, যা সরাসরি (কোনও অপ্টিমাইজেশন ছাড়াই) কন্ডিশনাল মুভ ইন্সট্রাকশনটিতে সংকলন করবে x86, এটি হল টের্নারি অপারেটর ... ? ... : ...। সুতরাং আমরা উপরের বিবৃতিটি একটি সমতুল্যতে আবার লিখি:

sum += data[c] >=128 ? data[c] : 0;

পঠনযোগ্যতা বজায় রাখার সময়, আমরা গতিবেগের উপাদানটি পরীক্ষা করতে পারি।

একটি ইন্টেল কোর আই 7 -2600 কে @ 3.4 গিগাহার্টজ এবং ভিজ্যুয়াল স্টুডিও 2010 রিলিজ মোডে, মানদণ্ডটি (মাইস্টিয়াল থেকে অনুলিপি করা ফর্ম্যাট):

এক্স 86

//  Branch - Random
seconds = 8.885

//  Branch - Sorted
seconds = 1.528

//  Branchless - Random
seconds = 3.716

//  Branchless - Sorted
seconds = 3.71

x64

//  Branch - Random
seconds = 11.302

//  Branch - Sorted
 seconds = 1.830

//  Branchless - Random
seconds = 2.736

//  Branchless - Sorted
seconds = 2.737

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

এখন আসুন x86তারা যে সমাবেশটি উত্পন্ন করে তা তদন্ত করে আরও নিবিড়ভাবে দেখুন । সরলতার জন্য, আমরা দুটি ফাংশন max1এবং ব্যবহার করি max2

max1শর্তসাপেক্ষ শাখা ব্যবহার করে if... else ...:

int max1(int a, int b) {
    if (a > b)
        return a;
    else
        return b;
}

max2টেরিনারি অপারেটর ব্যবহার করে ... ? ... : ...:

int max2(int a, int b) {
    return a > b ? a : b;
}

একটি x86-64 মেশিনে, GCC -Sনীচের সমাবেশটি উত্পন্ন করে।

:max1
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    -8(%rbp), %eax
    jle     .L2
    movl    -4(%rbp), %eax
    movl    %eax, -12(%rbp)
    jmp     .L4
.L2:
    movl    -8(%rbp), %eax
    movl    %eax, -12(%rbp)
.L4:
    movl    -12(%rbp), %eax
    leave
    ret

:max2
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    %eax, -8(%rbp)
    cmovge  -8(%rbp), %eax
    leave
    ret

max2নির্দেশের ব্যবহারের কারণে অনেক কম কোড ব্যবহার করে cmovge। তবে আসল লাভটি হ'ল max2শাখা লাফিয়ে জড়িত না jmp, যা পূর্বাভাসিত ফলাফলটি সঠিক না হলে একটি গুরুত্বপূর্ণ পারফরম্যান্স পেনাল্টি থাকে।

তাহলে শর্তযুক্ত পদক্ষেপটি আরও ভালভাবে সম্পাদন করে কেন?

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

একটি শাখা ক্ষেত্রে, নিম্নলিখিত নির্দেশ পূর্ববর্তী দ্বারা নির্ধারিত হয়, তাই আমরা পাইপলাইং করতে পারি না। আমাদের হয় অপেক্ষা করতে হবে বা ভবিষ্যদ্বাণী করতে হবে।

একটি শর্তাধীন পদক্ষেপ ক্ষেত্রে, ফাঁসি শর্তসাপেক্ষ পদক্ষেপ নির্দেশ বিভিন্ন পর্যায়ে বিভক্ত করা হয়, কিন্তু তার আগে পর্যায়ে পছন্দ Fetchএবং Decodeপূর্ববর্তী নির্দেশ ফল উপর নির্ভর করে না; শুধুমাত্র পরবর্তী পর্যায়ে ফলাফলের প্রয়োজন। সুতরাং, আমরা একটি নির্দেশের কার্যকর করার সময়ের একটি ভগ্নাংশ অপেক্ষা করি। ভবিষ্যদ্বাণী করা সহজ হলে শর্তসাপেক্ষে শর্তসাপেক্ষে সরানো সংস্করণটি ধীর হয়।

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

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


7
@ ব্লুরাজা-ড্যানিপ্লুঘুফুট এটি অন-অপ্টিমাইজড সংস্করণ। সংকলকটি টার্নারি-অপারেটরটিকে অনুকূলিত করে না, এটি কেবল এটি অনুবাদ করে। জিসিসি যদি যথোপযুক্ত অপ্টিমাইজেশনের স্তর দেওয়া হয় তবে তা অনুকূল করতে পারে, তবুও, এটি শর্তযুক্ত পদক্ষেপের শক্তি দেখায় এবং ম্যানুয়াল অপ্টিমাইজেশান একটি পার্থক্য করে।
WiSaGaN

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

55
@ উইসাগন আমার বিভ্রান্তিকর -O0উদাহরণটি মুছে ফেলতে এবং আপনার দুটি টেস্টকেসে অনুকূলিতৃত asm এর পার্থক্য দেখাতে যদি আপনার উত্তরটি পরিবর্তন করে তবে আমার ডাউনভোটটি অবশ্যই একটি উঁচুতে পরিণত হবে ।
জাস্টিন এল।

56
@ ইউপিএন্ডএডাম পরীক্ষার মুহুর্তে, ভিসি ২০০০ উচ্চ অপ্টিমাইজেশনের স্তর নির্দিষ্ট করার সময়ও শর্তসাপেক্ষে মূল শাখাকে অনুকূল করতে পারে না, যখন জিসিসি পারে।
WiSaGaN

9
এই টেরিনারি অপারেটর ট্রিক জাভাটির জন্য সুন্দরভাবে কাজ করে। মাইস্টিকালের উত্তরটি পড়ার পরে, আমি ভাবছিলাম যে জাভাতে ব্রাঞ্চের মিথ্যা ভবিষ্যদ্বাণী এড়াতে কী করা যেতে পারে যেহেতু জাভার কাছে -O3 এর সমান কিছু নেই। টার্নারি অপারেটর: 2.1943 এবং মূল: 6.0303 এস।
কিন চেং

2271

আপনি যদি এই কোডটিতে করা যেতে পারে এমন আরও আরও অনুকূলিতকরণ সম্পর্কে আগ্রহী হন তবে এটি বিবেচনা করুন:

মূল লুপ দিয়ে শুরু:

for (unsigned i = 0; i < 100000; ++i)
{
    for (unsigned j = 0; j < arraySize; ++j)
    {
        if (data[j] >= 128)
            sum += data[j];
    }
}

লুপ ইন্টারচেঞ্জের সাহায্যে আমরা নিরাপদে এই লুপটি এতে পরিবর্তন করতে পারি:

for (unsigned j = 0; j < arraySize; ++j)
{
    for (unsigned i = 0; i < 100000; ++i)
    {
        if (data[j] >= 128)
            sum += data[j];
    }
}

তারপরে, আপনি দেখতে পারেন যে লুপটি ifকার্যকর করার সময় শর্তসাপেক্ষ স্থির থাকে i, তাই আপনি ifআউট উত্তোলন করতে পারেন:

for (unsigned j = 0; j < arraySize; ++j)
{
    if (data[j] >= 128)
    {
        for (unsigned i = 0; i < 100000; ++i)
        {
            sum += data[j];
        }
    }
}

তারপরে, আপনি দেখতে পাচ্ছেন যে অভ্যন্তরীণ লুপটি একটি একক অভিব্যক্তিতে বিভক্ত হতে পারে, ধরে নেওয়া ধরে ভাসমান পয়েন্ট মডেল এটির অনুমতি দেয় ( /fp:fastউদাহরণস্বরূপ, নিক্ষেপ করা হয়)

for (unsigned j = 0; j < arraySize; ++j)
{
    if (data[j] >= 128)
    {
        sum += data[j] * 100000;
    }
}

এটি আগের চেয়ে 100,000 গুণ বেশি দ্রুত।


276
আপনি যদি প্রতারণা করতে চান তবে আপনি লুপের বাইরেও গুণটি নিতে পারেন এবং লুপের পরে যোগফল = = 100000 করতে পারেন।
জাইফ

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

54
লুপগুলি অদল-বদল করার সহজ স্পিরিটে না হলেও, ifএই বিন্দুটির অভ্যন্তরটি রূপান্তরিত হতে পারে: sum += (data[j] >= 128) ? data[j] * 100000 : 0;যা সংকলকটি হ্রাস করতে cmovgeবা সমমান করতে সক্ষম হতে পারে ।
অ্যালেক্স উত্তর-কীগুলি

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

34
@ সৌরহাইটস: ভুল প্রশ্ন: সংকলকটি লুপ সোয়াপ করবে না কেন। মাইক্রোবেঞ্চমার্কগুলি শক্ত;)
মিঃ

1884

সন্দেহ নেই যে আমাদের মধ্যে কিছু সিপিইউর শাখা-ভবিষ্যদ্বাণীকারীদের জন্য সমস্যাযুক্ত কোড সনাক্তকরণের পদ্ধতিগুলিতে আগ্রহী হবে। ভালগ্রাইন্ড সরঞ্জামটিতে cachegrindএকটি শাখা-ভবিষ্যদ্বাণী সিমুলেটর রয়েছে, --branch-sim=yesপতাকা ব্যবহার করে সক্ষম করা হয়েছে । এই প্রশ্নের উদাহরণগুলিতে এটি চালানো, বহিরাগত লুপের সংখ্যা 10000 কমে এবং সংকলিত সহ g++, এই ফলাফলগুলি দেয়:

সাজানো:

==32551== Branches:        656,645,130  (  656,609,208 cond +    35,922 ind)
==32551== Mispredicts:         169,556  (      169,095 cond +       461 ind)
==32551== Mispred rate:            0.0% (          0.0%     +       1.2%   )

পাঁচমিশালী:

==32555== Branches:        655,996,082  (  655,960,160 cond +  35,922 ind)
==32555== Mispredicts:     164,073,152  (  164,072,692 cond +     460 ind)
==32555== Mispred rate:           25.0% (         25.0%     +     1.2%   )

cg_annotateআমরা উত্পাদিত লাইন বাই লাইন আউটপুটটিতে নিচে ড্রিলিং আমরা দেখতে থাকা লুপটির জন্য দেখতে পাই:

সাজানো:

          Bc    Bcm Bi Bim
      10,001      4  0   0      for (unsigned i = 0; i < 10000; ++i)
           .      .  .   .      {
           .      .  .   .          // primary loop
 327,690,000 10,016  0   0          for (unsigned c = 0; c < arraySize; ++c)
           .      .  .   .          {
 327,680,000 10,006  0   0              if (data[c] >= 128)
           0      0  0   0                  sum += data[c];
           .      .  .   .          }
           .      .  .   .      }

পাঁচমিশালী:

          Bc         Bcm Bi Bim
      10,001           4  0   0      for (unsigned i = 0; i < 10000; ++i)
           .           .  .   .      {
           .           .  .   .          // primary loop
 327,690,000      10,038  0   0          for (unsigned c = 0; c < arraySize; ++c)
           .           .  .   .          {
 327,680,000 164,050,007  0   0              if (data[c] >= 128)
           0           0  0   0                  sum += data[c];
           .           .  .   .          }
           .           .  .   .      }

এটি আপনাকে সহজেই সমস্যাযুক্ত লাইনটি সনাক্ত করতে দেয় - if (data[c] >= 128)আনসোর্ট করা সংস্করণে লাইনটি Bcmক্যাশেগ্রিন্ডের ব্রাঞ্চ-প্রেডিক্টর মডেলের অধীনে 164,050,007 অপ্রতিকৃত শর্তাধীন শাখা ( ) তৈরি করে, যেখানে এটি কেবল বাছাই করা সংস্করণে 10,006 সৃষ্টি করে।


বিকল্প হিসাবে, লিনাক্সে আপনি একই কাজটি সম্পাদন করতে পারফরম্যান্স কাউন্টার সাবসিস্টেম ব্যবহার করতে পারেন, তবে সিপিইউ কাউন্টার ব্যবহার করে নেটিভ পারফরম্যান্স সহ।

perf stat ./sumtest_sorted

সাজানো:

 Performance counter stats for './sumtest_sorted':

  11808.095776 task-clock                #    0.998 CPUs utilized          
         1,062 context-switches          #    0.090 K/sec                  
            14 CPU-migrations            #    0.001 K/sec                  
           337 page-faults               #    0.029 K/sec                  
26,487,882,764 cycles                    #    2.243 GHz                    
41,025,654,322 instructions              #    1.55  insns per cycle        
 6,558,871,379 branches                  #  555.455 M/sec                  
       567,204 branch-misses             #    0.01% of all branches        

  11.827228330 seconds time elapsed

পাঁচমিশালী:

 Performance counter stats for './sumtest_unsorted':

  28877.954344 task-clock                #    0.998 CPUs utilized          
         2,584 context-switches          #    0.089 K/sec                  
            18 CPU-migrations            #    0.001 K/sec                  
           335 page-faults               #    0.012 K/sec                  
65,076,127,595 cycles                    #    2.253 GHz                    
41,032,528,741 instructions              #    0.63  insns per cycle        
 6,560,579,013 branches                  #  227.183 M/sec                  
 1,646,394,749 branch-misses             #   25.10% of all branches        

  28.935500947 seconds time elapsed

এটি বিযুক্তকরণের সাথে উত্স কোড টীকাটিও করতে পারে।

perf record -e branch-misses ./sumtest_unsorted
perf annotate -d sumtest_unsorted
 Percent |      Source code & Disassembly of sumtest_unsorted
------------------------------------------------
...
         :                      sum += data[c];
    0.00 :        400a1a:       mov    -0x14(%rbp),%eax
   39.97 :        400a1d:       mov    %eax,%eax
    5.31 :        400a1f:       mov    -0x20040(%rbp,%rax,4),%eax
    4.60 :        400a26:       cltq   
    0.00 :        400a28:       add    %rax,-0x30(%rbp)
...

দেখুন কর্মক্ষমতা টিউটোরিয়াল আরো বিস্তারিত জানার জন্য।


74
এটি ভীতিজনক, অরসেটেড তালিকায় অ্যাডের হিট হওয়ার 50% সম্ভাবনা থাকা উচিত। কোনওভাবে শাখার পূর্বাভাসের কেবল 25% মিস হার রয়েছে, এটি 50% মিসের চেয়ে আরও ভাল কীভাবে করতে পারে?
টালব্রিয়ান

128
@ قد.b.lo: 25% সমস্ত শাখার মধ্যে রয়েছে - লুপে দুটি শাখা রয়েছে, একটির জন্য data[c] >= 128(যার হিসাবে আপনার প্রস্তাব অনুসারে একটি 50% মিস রেট রয়েছে) এবং একটি লুপ শর্তের জন্য c < arraySizeযার ~ 0% মিস হার রয়েছে ।
ক্যাফে

1340

আমি কেবল এই প্রশ্ন এবং এর উত্তরগুলি পড়েছি এবং আমি অনুভব করছি যে একটি উত্তর অনুপস্থিত।

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

এই পদ্ধতির সাধারণভাবে কাজ করে যদি:

  1. এটি একটি ছোট টেবিল এবং সম্ভবত প্রসেসরে ক্যাশে হবে এবং
  2. আপনি জিনিসগুলি বেশ টানটান লুপে চালাচ্ছেন এবং / বা প্রসেসর ডেটা প্রিলোড করতে পারেন।

পটভূমি এবং কেন

প্রসেসরের দৃষ্টিকোণ থেকে, আপনার স্মৃতিশক্তি ধীর। গতির পার্থক্যের জন্য ক্ষতিপূরণ দিতে, আপনার প্রসেসরের (L1 / L2 ক্যাশে) কয়েকটি কেস তৈরি করা হয়। সুতরাং কল্পনা করুন যে আপনি আপনার দুর্দান্ত গণনা করছেন এবং এটি নির্ধারণ করুন যে আপনার একটি টুকরো স্মৃতি দরকার। প্রসেসর তার 'লোড' অপারেশনটি পেয়ে মেমরির টুকরোটি ক্যাশে লোড করে - এবং তারপরে বাকী গণনাগুলি করতে ক্যাশে ব্যবহার করে। স্মৃতি তুলনামূলকভাবে ধীর হওয়ায় এই 'লোড' আপনার প্রোগ্রামটিকে ধীর করে দেবে।

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

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

প্রথম জিনিস আমরা জানি যা করতে হবে তা হল ছোট ? যদিও ছোট সাধারণত ভাল হয়, তবে থাম্বের একটি নিয়ম হ'ল <= 4096 বাইট আকারের টেবিলগুলির সাথে তাল মিলানো। উপরের সীমা হিসাবে: যদি আপনার অনুসন্ধানের টেবিলটি 64 কে এর চেয়ে বড় হয় তবে এটি সম্ভবত পুনর্বিবেচনা করার মতো।

একটি টেবিল নির্মাণ

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

এই ক্ষেত্রে:> = 128 এর অর্থ আমরা মানটি রাখতে পারি, <128 এর অর্থ আমরা এ থেকে মুক্তি পেয়েছি। এটি করার সহজতম উপায় হ'ল 'এবং' ব্যবহার করে: যদি আমরা এটি রাখি, আমরা এবং এটি 7FFFFFF সহ; যদি আমরা এটি থেকে মুক্তি পেতে চাই, আমরা এবং এটি 0 দিয়ে লক্ষ্য করুন যে 128 2 এর শক্তি 2 তাই আমরা এগিয়ে গিয়ে 32768/128 পূর্ণসংখ্যার একটি টেবিল তৈরি করতে পারি এবং এটি একটি শূন্য এবং প্রচুর পরিমাণে পূরণ করতে পারি 7FFFFFFFF আছে।

পরিচালিত ভাষা

আপনি ভাবতে পারেন কেন এটি পরিচালিত ভাষাগুলিতে ভাল কাজ করে। সর্বোপরি, পরিচালিত ভাষাগুলি আপনাকে কোনও গোলমাল করবেন না তা নিশ্চিত করার জন্য একটি শাখার সাথে অ্যারের সীমানা পরীক্ষা করে ...

ঠিক আছে, ঠিক নয় ... :-)

পরিচালিত ভাষার জন্য এই শাখাটি মুছে ফেলার বিষয়ে বেশ কিছু কাজ হয়েছে। উদাহরণ স্বরূপ:

for (int i = 0; i < array.Length; ++i)
{
   // Use array[i]
}

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

যদি আপনি পরিচালিত ভাষাগুলিতে লকআপগুলি নিয়ে সমস্যায় পড়ে থাকেন - & 0x[something]FFFসীমাটি চেকটিকে পূর্বাভাসযোগ্য করে তোলার জন্য কীটি আপনার লুকিং ফাংশনে একটি যোগ করা - এবং এটি আরও দ্রুত যেতে দেখবে।

এই মামলার ফলাফল

// Generate data
int arraySize = 32768;
int[] data = new int[arraySize];

Random random = new Random(0);
for (int c = 0; c < arraySize; ++c)
{
    data[c] = random.Next(256);
}

/*To keep the spirit of the code intact, I'll make a separate lookup table
(I assume we cannot modify 'data' or the number of loops)*/

int[] lookup = new int[256];

for (int c = 0; c < 256; ++c)
{
    lookup[c] = (c >= 128) ? c : 0;
}

// Test
DateTime startTime = System.DateTime.Now;
long sum = 0;

for (int i = 0; i < 100000; ++i)
{
    // Primary loop
    for (int j = 0; j < arraySize; ++j)
    {
        /* Here you basically want to use simple operations - so no
        random branches, but things like &, |, *, -, +, etc. are fine. */
        sum += lookup[data[j]];
    }
}

DateTime endTime = System.DateTime.Now;
Console.WriteLine(endTime - startTime);
Console.WriteLine("sum = " + sum);
Console.ReadLine();

57
আপনি শাখা-ভবিষ্যদ্বাণীকে বাইপাস করতে চান, কেন? এটি একটি অপ্টিমাইজেশন।
ডাস্টিন ওরেপা

108
কারণ কোনও শাখা শাখার চেয়ে ভাল নয় :-) অনেক পরিস্থিতিতে এটি খুব দ্রুত হয় ... আপনি যদি অনুকূলিত হন তবে অবশ্যই এটি চেষ্টা করার মতো। তারা এটিকে f.ex এ বেশ খানিকটা ব্যবহার করে গ্রাফিক্স.স্তানফোর্ড.এডু
সেয়ান্ডার

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

38
জেইন আপনি যদি সত্যিই জানতে চান ... হ্যাঁ: শাখাটি সহ 15 সেকেন্ড এবং আমার সংস্করণ সহ 10 টি। নির্বিশেষে, এটি কোনও উপায়ে জানা দরকারী কৌশল।
অ্যাটলাস্ট

42
কেন নয় sum += lookup[data[j]]যেখানে lookup256 এন্ট্রি সহ একটি বিন্যাস, প্রথম বেশী শূন্য হচ্ছে এবং শেষ বেশী সূচক সমান হচ্ছে?
ক্রিস ভ্যান্ডারমোটেন

1200

অ্যারে বাছাই করা হয় যখন তথ্য 0 এবং 255 এর মধ্যে বিতরণ করা হয়, পুনরাবৃত্তির প্রথমার্ধের চারপাশে if- ifস্টেটমেন্ট প্রবেশ করবে না ( বিবৃতিটি নীচে ভাগ করা হয়েছে)।

if (data[c] >= 128)
    sum += data[c];

প্রশ্নটি হ'ল: উপরোক্ত বিবৃতিটি সাজানো তথ্যের ক্ষেত্রে নির্দিষ্ট ক্ষেত্রে কার্যকর করা হয় না? এখানে "শাখার ভবিষ্যদ্বাণী" আসে। একটি শাখা ভবিষ্যদ্বাণী একটি ডিজিটাল সার্কিট যা অনুমান করার চেষ্টা করে যে কোনও শাখা (উদাহরণস্বরূপ কোনও if-then-elseকাঠামো) এটি নিশ্চিতভাবে পরিচিত হওয়ার আগেই যাবে। শাখার ভবিষ্যদ্বাণীকারীর উদ্দেশ্য হ'ল নির্দেশ পাইপলাইনে প্রবাহকে উন্নত করা। শাখার ভবিষ্যদ্বাণীকারীরা উচ্চ কার্যকর কার্যকারিতা অর্জনে গুরুত্বপূর্ণ ভূমিকা পালন করে!

এটি আরও ভালভাবে বুঝতে কিছু বেঞ্চ চিহ্নিত করা যাক

ifস্টেটমেন্টের পারফরম্যান্স তার অবস্থার পূর্বাভাসযোগ্য প্যাটার্ন রয়েছে কিনা তার উপর নির্ভর করে। যদি শর্তটি সর্বদা সত্য বা সর্বদা মিথ্যা থাকে তবে প্রসেসরের শাখার পূর্বাভাস যুক্তি প্যাটার্নটি বেছে নেবে। অন্যদিকে, প্যাটার্নটি যদি অনির্দেশ্য হয় তবে ifস্টেস্টমেন্টটি আরও ব্যয়বহুল হবে।

আসুন এই লুপটির কার্যকারিতাটি বিভিন্ন শর্তের সাথে পরিমাপ করি:

for (int i = 0; i < max; i++)
    if (condition)
        sum++;

বিভিন্ন সত্য-মিথ্যা নিদর্শনগুলির সাথে লুপের সময় এখানে রয়েছে:

Condition                Pattern             Time (ms)
-------------------------------------------------------
(i & 0×80000000) == 0    T repeated          322

(i & 0xffffffff) == 0    F repeated          276

(i & 1) == 0             TF alternating      760

(i & 3) == 0             TFFFTFFF           513

(i & 2) == 0             TTFFTTFF           1675

(i & 4) == 0             TTTTFFFFTTTTFFFF   1275

(i & 8) == 0             8T 8F 8T 8F        752

(i & 16) == 0            16T 16F 16T 16F    490

একটি " খারাপ " সত্য-মিথ্যা প্যাটার্ন ifএকটি " ভাল " প্যাটার্নের চেয়ে ছয় গুণ কম ধীরে ধীরে স্টেস্টমেন্ট তৈরি করতে পারে ! অবশ্যই, কোন প্যাটার্নটি ভাল এবং কোনটি খারাপ তা সংকলক দ্বারা উত্পাদিত সঠিক নির্দেশাবলীর উপর এবং নির্দিষ্ট প্রসেসরের উপর নির্ভর করে।

সুতরাং কার্য সম্পাদনের উপর শাখা পূর্বাভাসের প্রভাব সম্পর্কে কোনও সন্দেহ নেই!


23
@ মুভিংডাক 'কারণ এতে কোনও তফাত আসবে না - যে মানটি যে কোনও হতে পারে, তবে এটি এখনও এই প্রান্তিকের সীমানায় থাকবে। সুতরাং যখন আপনি ইতিমধ্যে সীমাটি জানেন তখন কেন এলোমেলো মান দেখান? যদিও আমি সম্মত হই যে আপনি সম্পূর্ণতার জন্য, এবং 'কেবল এটির জন্য' প্রদর্শন করতে পারেন।
সিএসটি 1992

24
@ সিএসটি ১৯৯২: এই মুহূর্তে তার ধীরতম সময়টি টিটিএফএফটিটিটিএফএফটিটিএফএফ, যা আমার মানব চোখে মনে হয়, এটি বেশ অনুমানযোগ্য। র্যান্ডম সহজাতভাবে অনির্দেশ্য, সুতরাং এটি ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে। ওও, এটি হতে পারে টিটিএফএফটিটিএফএফ পুরোপুরি প্যাথলজিকাল কেসে আঘাত করে। বলতে পারবেন না, যেহেতু তিনি এলোমেলো সময় দেওয়ার সময়টি প্রদর্শন করেন নি।
মাকিং হাঁস

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

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

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

1126

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

তবে এই ক্ষেত্রে, আমরা জানি মানগুলি [0, 255] এর মধ্যে থাকে এবং আমরা কেবল মানগুলি> = 128 কেই যত্নশীল করি That এর অর্থ আমরা সহজেই একটি বিট বের করতে পারি যা আমাদের মান চায় কিনা তা আমাদের বলবে: স্থানান্তরিত করে ডান b বিটের ডেটা, আমরা একটি 0 বিট বা 1 বিট দিয়ে রেখেছি এবং আমরা যখন 1 বিট থাকি তখনই আমরা মানটি যুক্ত করতে চাই। এই বিটটিকে "সিদ্ধান্ত বিট" বলি।

অ্যারেতে সূচি হিসাবে সিদ্ধান্ত বিটের 0/1 মান ব্যবহার করে আমরা কোড তৈরি করতে পারি যা ডেটা বাছাই করা হয় বা না সাজানো হয় তা সমান দ্রুত হবে। আমাদের কোড সর্বদা একটি মান যুক্ত করবে, কিন্তু যখন সিদ্ধান্ত বিট 0 হয়, আমরা সেই জায়গাতেই আমাদের মূল্যবোধ করব না। কোডটি এখানে:

// Test
clock_t start = clock();
long long a[] = {0, 0};
long long sum;

for (unsigned i = 0; i < 100000; ++i)
{
    // Primary loop
    for (unsigned c = 0; c < arraySize; ++c)
    {
        int j = (data[c] >> 7);
        a[j] += data[c];
    }
}

double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
sum = a[1];

এই কোডটি অ্যাডগুলির অর্ধেক অপচয় করে তবে কোনও শাখার পূর্বাভাস ব্যর্থ হয় না। এটি সত্যিকারের if স্টেটমেন্ট সহ ভার্সনের তুলনায় এলোমেলো ডেটাতে অত্যন্ত দ্রুত।

তবে আমার পরীক্ষায়, একটি স্পষ্টতূপে অনুসন্ধানের টেবিলটি এর চেয়ে কিছুটা দ্রুত ছিল সম্ভবত কারণ দেখার জন্য সারণিতে সূচিটি কিছুটা স্থানান্তরিত করার চেয়ে কিছুটা দ্রুত ছিল। এটি দেখায় যে কীভাবে আমার কোডটি সেট আপ করে এবং lutলুকিং টেবিলটি ব্যবহার করে ( কোডটিতে অকল্পনীয়ভাবে " লুকআপ টেবিল " বলা হয় )। এখানে সি ++ কোডটি রয়েছে:

// Declare and then fill in the lookup table
int lut[256];
for (unsigned c = 0; c < 256; ++c)
    lut[c] = (c >= 128) ? c : 0;

// Use the lookup table after it is built
for (unsigned i = 0; i < 100000; ++i)
{
    // Primary loop
    for (unsigned c = 0; c < arraySize; ++c)
    {
        sum += lut[data[c]];
    }
}

এই ক্ষেত্রে, সন্ধানের টেবিলটি কেবল 256 বাইট ছিল, সুতরাং এটি একটি ক্যাশে চমৎকারভাবে ফিট করে এবং সমস্ত দ্রুত ছিল। যদি ডেটা 24-বিট মান হয় এবং এই কৌশলটি কার্যকর হবে না এবং আমরা কেবল তার অর্ধেক চেয়েছিলাম ... দেখার জন্য টেবিলটি ব্যবহারিক হওয়ার চেয়ে অনেক বড় হবে। অন্যদিকে, আমরা উপরে প্রদর্শিত দুটি কৌশল একত্রিত করতে পারি: প্রথমে বিটগুলি স্থানান্তরিত করুন, তারপরে একটি সন্ধানের টেবিলটি সূচী করুন। আমরা কেবলমাত্র শীর্ষ অর্ধেক মান চাই এমন একটি 24-বিট মানের জন্য, আমরা 12 বিট দ্বারা সম্ভাব্যভাবে ডানটি স্থানান্তর করতে পারি এবং টেবিল সূচকের জন্য 12-বিট মান রেখে যেতে পারি। একটি 12-বিট টেবিল সূচক 4096 মানগুলির একটি সারণিকে বোঝায় যা ব্যবহারিক হতে পারে।

কোন ifবিবৃতি ব্যবহারের পরিবর্তে কোন অ্যারেতে ইনডেক্স করার কৌশলটি কোন পয়েন্টারটি ব্যবহার করবেন তা সিদ্ধান্ত নেওয়ার জন্য ব্যবহার করা যেতে পারে। আমি একটি লাইব্রেরি দেখেছি যা বাইনারি গাছ প্রয়োগ করে এবং দুটি নামকৃত পয়েন্টার ( pLeftএবং pRightবা যাই হোক না কেন) এর পরিবর্তে দৈর্ঘ্যের 2 পয়েন্টার থাকে এবং কোনটি অনুসরণ করা উচিত তা সিদ্ধান্ত নিতে "সিদ্ধান্ত বিট" কৌশলটি ব্যবহার করে। উদাহরণস্বরূপ, পরিবর্তে:

if (x < node->value)
    node = node->pLeft;
else
    node = node->pRight;

এই গ্রন্থাগারটি এমন কিছু করবে:

i = (x < node->value);
node = node->link[i];

এই কোডটির একটি লিঙ্ক এখানে: লাল কালো গাছগুলি , চিরতরে বিভ্রান্ত


29
ঠিক আছে, আপনি কেবল সরাসরি বিটটি ব্যবহার করতে পারেন এবং গুণ করতে পারেন ( data[c]>>7- যা এখানেও কোথাও আলোচনা করা হয়েছে); আমি ইচ্ছাকৃতভাবে এই সমাধানটি বাদ দিয়েছি, তবে অবশ্যই আপনি সঠিক। কেবলমাত্র একটি ছোট দ্রষ্টব্য: সন্ধানের টেবিলগুলির জন্য থাম্বের নিয়মটি হ'ল এটি যদি 4KB- এ ফিট করে (ক্যাশিংয়ের কারণে), তবে এটি কাজ করবে - টেবিলটিকে যতটা সম্ভব ছোট করে তোলে। পরিচালিত ভাষার জন্য আমি এটিকে that৪ কেবি-তে ঠেকাব, সি ++ এবং সি-এর মতো নিম্ন-স্তরের ভাষার জন্য, আমি সম্ভবত পুনর্বিবেচনা করব (এটি কেবল আমার অভিজ্ঞতা)। যেহেতু typeof(int) = 4, আমি সর্বোচ্চ 10 বিটগুলিতে আটকে থাকার চেষ্টা করব।
অ্যাটলাস্ট

17
আমি মনে করি 0/1 মানের সাথে সূচকগুলি সম্ভবত পূর্ণসংখ্যার গুণকের চেয়ে দ্রুত হবে তবে আমার ধারণা যদি পারফরম্যান্সটি সত্যই সমালোচিত হয় তবে এটির প্রোফাইল দেওয়া উচিত। আমি সম্মত হচ্ছি যে ক্যাশের চাপ এড়ানোর জন্য ছোট ছোট সন্ধানের টেবিলগুলি অপরিহার্য, তবে স্পষ্টতই যদি আপনার কাছে আরও বড় ক্যাশে থাকে তবে আপনি বড় লকিং টেবিলটি নিয়ে চলে যেতে পারেন, সুতরাং 4KB একটি শক্ত নিয়মের চেয়ে থাম্বের নিয়ম। আমার মনে হয় তুমি বোঝাতে চাইছ sizeof(int) == 4? এটি 32-বিটের জন্য সত্য হবে। আমার দুই বছরের পুরাতন সেল ফোনে একটি 32 কেবি এল 1 ক্যাশে রয়েছে, তাই 4K লক টেবিলটিও কাজ করতে পারে, বিশেষত যদি লুকিং মানগুলি কোনও int এর পরিবর্তে বাইট হয়।
স্টিভেহা

12
সম্ভবত আমি কিছু মিস করছি তবে আপনার j0 বা 1 পদ্ধতির সমান কেন আপনি jঅ্যারের ইনডেক্সিং ব্যবহার না করে যুক্ত করার আগে নিজের মানটি কেবল গুণন করবেন না (সম্ভবত এর 1-jচেয়ে গুণফল করা উচিত j)
রিচার্ড টিংল

6
@ স্টেভেহা গুণ বহুগুণ হওয়া উচিত, আমি এটি ইন্টেলের বইগুলিতে সন্ধান করার চেষ্টা করেছি, কিন্তু এটি খুঁজে পেলাম না ... যেভাবেই হোক, বেঞ্চমার্কিং আমাকে এখানে ফলাফলও দেয়।
আটলস্টে

10
@ স্টেভেহা পিএস: আর একটি সম্ভাব্য উত্তর হ'ল int c = data[j]; sum += c & -(c >> 7);যার কোনও গুনের প্রয়োজন নেই।
আটলস্টে

1021

বাছাই করা ক্ষেত্রে, আপনি সফল শাখার পূর্বাভাস বা কোনও শাখাবিহীন তুলনা কৌশলের উপর নির্ভর করার চেয়ে ভাল করতে পারেন: সম্পূর্ণ শাখাটি মুছে ফেলুন।

প্রকৃতপক্ষে, অ্যারেটি একটি স্বতন্ত্র জোনে data < 128এবং অন্য সাথে বিভক্ত data >= 128। সুতরাং আপনার দ্বিগোটমিক অনুসন্ধান (ব্যবহার করে) পার্টিশন পয়েন্টটি খুঁজে পাওয়া উচিতLg(arraySize) = 15 তুলনা করে) , তারপরে সেই বিন্দু থেকে সরাসরি সংগ্রহ করুন।

এর মতো কিছু (চেক করা হয়নি)

int i= 0, j, k= arraySize;
while (i < k)
{
  j= (i + k) >> 1;
  if (data[j] >= 128)
    k= j;
  else
    i= j;
}
sum= 0;
for (; i < arraySize; i++)
  sum+= data[i];

বা, আরও কিছুটা নিবিড়

int i, k, j= (i + k) >> 1;
for (i= 0, k= arraySize; i < k; (data[j] >= 128 ? k : i)= j)
  j= (i + k) >> 1;
for (sum= 0; i < arraySize; i++)
  sum+= data[i];

এখনও একটি দ্রুত পন্থা, এটি বাছাই করা বা অরক্ষিত উভয়ের জন্য একটি আনুমানিক সমাধান দেয় : sum= 3137536;(সত্যিকারের ইউনিফর্ম বিতরণ ধরে নেওয়া, 191.5 এর প্রত্যাশিত মান সহ) :-)


23
sum= 3137536- চালাক স্পষ্টতই প্রশ্নটি বিন্দু নয়। প্রশ্নটি অবাক করা পারফরম্যান্সের বৈশিষ্ট্যগুলি ব্যাখ্যা করার বিষয়ে clearly আমি বলতে আগ্রহী যে std::partitionপরিবর্তে std::sortকরা যুক্তটি মূল্যবান। যদিও আসল প্রশ্নটি কেবল দেওয়া সিন্থেটিক বেঞ্চমার্কের চেয়ে বেশি প্রসারিত।
sehe

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

831

উপরের আচরণটি শাখার পূর্বাভাসের কারণে ঘটছে।

শাখার পূর্বাভাস বুঝতে প্রথমে নির্দেশ পাইপলাইন বুঝতে হবে :

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

সাধারণত, আধুনিক প্রসেসরের বেশ দীর্ঘ দীর্ঘ পাইপলাইন রয়েছে তবে স্বাচ্ছন্দ্যের জন্য আসুন কেবল এই 4 টি পদক্ষেপ বিবেচনা করুন।

  1. IF - মেমরি থেকে নির্দেশ আনুন
  2. আইডি - নির্দেশটি ডিকোড করুন
  3. প্রাক্তন - নির্দেশনা কার্যকর করুন
  4. ডাব্লুবি - সিপিইউ রেজিস্ট্রারে ফিরে লিখুন

2 নির্দেশাবলীর জন্য সাধারণভাবে 4-পর্যায়ের পাইপলাইন। সাধারণভাবে 4-পর্যায়ের পাইপলাইন

উপরের প্রশ্নে ফিরে যাওয়া যাক নিম্নলিখিত নির্দেশাবলী বিবেচনা করুন:

                        A) if (data[c] >= 128)
                                /\
                               /  \
                              /    \
                        true /      \ false
                            /        \
                           /          \
                          /            \
                         /              \
              B) sum += data[c];          C) for loop or print().

শাখার পূর্বাভাস ব্যতীত নিম্নলিখিতটি ঘটবে:

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

শর্তটি সত্য হলে কখন: এখানে চিত্র বর্ণনা লিখুন

যখন শর্তটি মিথ্যা ফিরে আসে: এখানে চিত্র বর্ণনা লিখুন

নির্দেশের ফলাফলের অপেক্ষার ফলস্বরূপ, উপরের ক্ষেত্রে ব্যয় করা মোট সিপিইউ চক্র (শাখার পূর্বাভাস ব্যতীত; সত্য এবং মিথ্যা উভয়ের জন্য) 7।

তাহলে শাখার পূর্বাভাস কী?

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

সঠিক অনুমানের ক্ষেত্রে পাইপলাইনটি দেখতে এরকম কিছু দেখাচ্ছে: এখানে চিত্র বর্ণনা লিখুন

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

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

  1. সমস্ত উপাদান 128 এরও কম
  2. সমস্ত উপাদানগুলি 128 এর চেয়ে বেশি
  3. কিছু সূচনা নতুন উপাদানগুলি 128 এরও কম এবং পরে এটি 128 এরও বেশি হয়ে যায়

আসুন আমরা ধরে নিই যে ভবিষ্যদ্বাণীকারী সর্বদা প্রথম রানে সত্য শাখা ধরে রাখবে।

সুতরাং প্রথম ক্ষেত্রে, এটি সর্বদা সত্য শাখা গ্রহণ করবে যেহেতু icallyতিহাসিকভাবে এর সমস্ত পূর্বাভাস সঠিক। ২ য় ক্ষেত্রে, প্রাথমিকভাবে এটি ভুল পূর্বাভাস দেবে, তবে কয়েকটি পুনরাবৃত্তির পরে এটি সঠিকভাবে ভবিষ্যদ্বাণী করবে। তৃতীয় ক্ষেত্রে, এটি প্রাথমিকভাবে 128 এর কম উপাদান না হওয়া পর্যন্ত সঠিকভাবে পূর্বাভাস দেবে After এর পরে এটি কিছু সময়ের জন্য ব্যর্থ হবে এবং যখন এটি ইতিহাসে শাখার পূর্বাভাস ব্যর্থতা দেখবে তখন এটি সঠিক হয়ে যাবে।

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

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


1
দুটি নির্দেশাবলী একসাথে কার্যকর করা হয় কিভাবে? এটি পৃথক সিপিইউ কোর দিয়ে করা হয়েছে বা পাইপলাইন নির্দেশটি একক সিপিইউ কোরতে সংহত হয়েছে?
এমকাজেম আখগারি

1
@ এমকাজেমআখগারি এটি সমস্তই একটি যৌক্তিক মূলের ভিতরে। আপনি যদি আগ্রহী হন তবে এটি ইন্টেল সফটওয়্যার বিকাশকারী ম্যানুয়াল
সের্গেই.কুইক্সোটিক্যাক্সিস

727

একটি সরকারী উত্তর থেকে হবে

  1. ইন্টেল - শাখার ভুল অনুমানের মূল্য এড়ানো
  2. ইন্টেল - ভুল প্রতিরোধগুলি রোধ করতে শাখা এবং লুপ পুনঃনির্মাণ
  3. বৈজ্ঞানিক কাগজপত্র - শাখার পূর্বাভাস কম্পিউটার আর্কিটেকচার
  4. বই: জে এল হেনেসি, ডিএ প্যাটারসন: কম্পিউটার আর্কিটেকচার: একটি পরিমাণগত পদ্ধতির
  5. বৈজ্ঞানিক প্রকাশনাতে নিবন্ধগুলি: টিওয়াই ইয়ে, ওয়াইএন প্যাট শাখার পূর্বাভাসে এগুলি অনেক কিছু করেছিলেন।

এই সুন্দর চিত্রটি থেকে আপনি দেখতে পাবেন কেন শাখার ভবিষ্যদ্বাণী বিভ্রান্ত হয়।

2-বিট স্টেট ডায়াগ্রাম

মূল কোডের প্রতিটি উপাদান একটি এলোমেলো মান

data[c] = std::rand() % 256;

সুতরাং ভবিষ্যদ্বাণী std::rand()ঘা হিসাবে পক্ষ পরিবর্তন করবে ।

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



696

একই লাইনে (আমি মনে করি এটি কোনও উত্তর দ্বারা হাইলাইট করা হয়নি) এটি উল্লেখ করা ভাল যে কখনও কখনও (বিশেষত সফ্টওয়্যার যেখানে কর্মক্ষমতা গুরুত্বপূর্ণ the যেমন লিনাক্স কার্নেলের মতো) আপনি নীচের মত বিবৃতি পাওয়া গেলে কিছু খুঁজে পেতে পারেন:

if (likely( everything_is_ok ))
{
    /* Do something */
}

বা অনুরূপ:

if (unlikely(very_improbable_condition))
{
    /* Do something */    
}

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

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


678

সি ++ এ প্রায়শই ব্যবহৃত বুলিয়ান অপারেশন সংকলিত প্রোগ্রামে অনেকগুলি শাখা তৈরি করে। যদি এই শাখাগুলি লুপগুলির অভ্যন্তরে থাকে এবং ভবিষ্যদ্বাণী করা কঠিন হয় তবে এগুলি কার্যকরভাবে কার্যকর করতে ধীর করতে পারে। বুলিয়ান ভেরিয়েবল যেমন মান 8-বিট ইন্টিজার সংরক্ষণ করা হয় 0জন্য falseএবং 1জন্য true

বুলিয়ান ভেরিয়েবল অর্থে overdetermined করছেন যে সব অপারেটরদের ইনপুট চেক যেমন বুলিয়ান ভেরিয়েবল আছে যদি ইনপুট ছাড়া অন্য মূল্য আছে 0বা 1, কিন্তু অপারেটার আউটপুট আছে Booleans চেয়ে অন্যান্য মান তৈরী করতে পারে 0বা 1। এটি বুলিয়ান ভেরিয়েবলগুলির সাথে অপারেশনকে প্রয়োজনীয়তার চেয়ে কম দক্ষ হিসাবে ইনপুট হিসাবে তোলে। উদাহরণ বিবেচনা করুন:

bool a, b, c, d;
c = a && b;
d = a || b;

এটি সাধারণত নিম্নলিখিতভাবে সংকলক দ্বারা প্রয়োগ করা হয়:

bool a, b, c, d;
if (a != 0) {
    if (b != 0) {
        c = 1;
    }
    else {
        goto CFALSE;
    }
}
else {
    CFALSE:
    c = 0;
}
if (a == 0) {
    if (b == 0) {
        d = 0;
    }
    else {
        goto DTRUE;
    }
}
else {
    DTRUE:
    d = 1;
}

এই কোডটি সর্বোত্তম থেকে অনেক দূরে। ভুল অনুমানের ক্ষেত্রে শাখাগুলি দীর্ঘ সময় নিতে পারে। বুলিয়ান অপারেশনগুলিকে আরও দক্ষ করে তোলা যেতে পারে যদি এটি দৃty়তার সাথে জানা যায় যে অপারেশনগুলি 0এবং এর বাইরে অন্য কোনও মান নেই 1। সংকলকটি এইরকম অনুমান না করার কারণটি হ'ল ভেরিয়েবলগুলি অনির্বাচিত বা অজানা উত্স থেকে আসা হলে অন্যান্য মান থাকতে পারে। উপরের কোড যদি অপ্টিমাইজ করা যেতে পারে aএবং bবৈধ মান সক্রিয়া হয়েছে বা যদি তারা অপারেটার যে বুলিয়ান আউটপুট উত্পাদন থেকে আসা। অনুকূলিত কোডটি এর মতো দেখাচ্ছে:

char a = 0, b = 1, c, d;
c = a & b;
d = a | b;

charবুলিয়ান অপারেটর ( এবং ) এর পরিবর্তে boolবিটওয়াইস অপারেটরগুলি ( &এবং |) ব্যবহার করা সম্ভব করার পরিবর্তে ব্যবহার করা হয় । বিটওয়াইজ অপারেটরগুলি একক নির্দেশ যা কেবল একটি ঘড়ি চক্র নেয়। বা অপারেটর ( ) কাজ করে এমনকি যদি এবং ছাড়া অন্য মান বা । অ্যান্ড অপারেটর ( ) এবং এক্সক্লুসিভ বা অপারেটর ( ) অপারেন্ডদের এবং এর চেয়ে অন্যান্য মান থাকলে অসম্পূর্ণ ফলাফল দিতে পারে ।&&|||ab01&^01

~নোটের জন্য ব্যবহার করা যাবে না। পরিবর্তে, আপনি যে ভেরিয়েবলটি 0বা 1এটির সাথে এক্সওর'র মাধ্যমে পরিচিত তা বুলিয়ান নট তৈরি করতে পারেন 1:

bool a, b;
b = !a;

এতে অনুকূলিত হতে পারে:

char a = 0, b;
b = a ^ 1;

a && bএর সাথে প্রতিস্থাপন করা যায় না a & bযদি bএমন একটি অভিব্যক্তি হয় যা যদি aহয় তবে মূল্যায়ন করা উচিত নয় false( &&হবে মূল্যায়ন করবে না b, &হবে)। তেমনি, এর a || bসাথে প্রতিস্থাপন করা যায় না a | bযদি bএমন একটি অভিব্যক্তি হয় যা যদি aহয় তবে মূল্যায়ন করা উচিত নয় true

অপারেটরগুলি তুলনামূলক তুলনায় অপারেশনগুলি পরিবর্তনশীল হলে বিটওয়াইয়া অপারেটরগুলি ব্যবহার করা আরও সুবিধাজনক:

bool a; double x, y, z;
a = x > y && z < 5.0;

বেশিরভাগ ক্ষেত্রেই অনুকূল (যদি আপনি &&এক্সপ্রেশনটি অনেকগুলি শাখার ভুল ধারণা তৈরি করার প্রত্যাশা না করেন )।


341

এটা সত্যি!...

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

যদি অ্যারে বাছাই করা হয়, আপনার পদক্ষেপটি প্রথম ধাপে মিথ্যা: data[c] >= 128তবে রাস্তার শেষ পর্যন্ত পুরো পথটির জন্য সত্যিকারের মান হয়ে যায়। এভাবেই আপনি যুক্তির শেষের দিকে দ্রুত পৌঁছে যান। অন্যদিকে, একটি অরসোর্টড অ্যারে ব্যবহার করে, আপনার প্রচুর বাঁক এবং প্রক্রিয়াজাতকরণ প্রয়োজন যা আপনার কোডটি নিশ্চিতর জন্য ধীর করে দিন ...

নীচে আপনার জন্য তৈরি করা চিত্রটি দেখুন। কোন রাস্তায় দ্রুত শেষ হতে চলেছে?

শাখার পূর্বাভাস

সুতরাং প্রোগ্রামগতভাবে, শাখার পূর্বাভাস প্রক্রিয়াটি ধীর হওয়ার কারণ ...

এছাড়াও শেষে, জেনে রাখা ভাল যে আমাদের দুটি ধরণের শাখা পূর্বাভাস রয়েছে যে প্রত্যেকে আপনার কোডকে আলাদাভাবে প্রভাবিত করবে:

1. স্ট্যাটিক

2. গতিশীল

শাখার পূর্বাভাস

স্থিতিশীল শাখার পূর্বাভাস মাইক্রোপ্রসেসর দ্বারা প্রথমবার শর্তযুক্ত শাখার মুখোমুখি হয় এবং শর্তাধীন শাখা কোড কার্যকর করার জন্য গতিশীল শাখার পূর্বাভাস ব্যবহার করা হয়।

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


304

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

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

লিঙ্কটি এখানে: http://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/profile/demo.htm


3
এটি একটি খুব আকর্ষণীয় নিবন্ধ (আসলে, আমি কেবল এটি সব পড়েছি), তবে এটি কীভাবে প্রশ্নের উত্তর দেয়?
পিটার মর্টেনসেন

2
@ পিটারমোরটেনসেন আপনার প্রশ্নে আমি খানিকটা ঝাপটায় আছি। উদাহরণস্বরূপ, এখানে অংশটি থেকে একটি প্রাসঙ্গিক লাইন: When the input is unsorted, all the rest of the loop takes substantial time. But with sorted input, the processor is somehow able to spend not just less time in the body of the loop, meaning the buckets at offsets 0x18 and 0x1C, but vanishingly little time on the mechanism of looping. লেখক এখানে পোস্ট করা কোডের প্রসঙ্গে প্রোফাইলিং নিয়ে আলোচনা করার চেষ্টা করছেন এবং কেন বাছাই করা মামলাটি এত দ্রুত কেন তা ব্যাখ্যা করার চেষ্টা করছেন।
16:38

260

অন্যদের দ্বারা ইতিমধ্যে যা উল্লেখ করা হয়েছে, রহস্যের পিছনে যা রয়েছে তা ব্রাঞ্চ প্রেডিক্টর

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

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

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

চিত্র 1

বর্ণিত দৃশ্যের উপর ভিত্তি করে, বিভিন্ন পরিস্থিতিতে কীভাবে পাইপলাইনে নির্দেশাবলী কার্যকর করা হয় তা দেখানোর জন্য আমি একটি অ্যানিমেশন ডেমো লিখেছি।

  1. শাখা প্রেডিক্টর ছাড়া।

শাখার পূর্বাভাস ব্যতীত, পরবর্তী নির্দেশটি পাইপলাইনে আনার পর্যায়ে প্রবেশের আগে শর্তসাপেক্ষে লাফ নির্দেশ নির্বাহের পর্যায়ে চলে না যাওয়া পর্যন্ত প্রসেসরের অপেক্ষা করতে হবে।

উদাহরণটিতে তিনটি নির্দেশাবলী রয়েছে এবং প্রথমটি হ'ল শর্তযুক্ত জাম্প নির্দেশ। শর্তসাপূর্ণ জাম্প নির্দেশনা কার্যকর না হওয়া পর্যন্ত পরবর্তী দুটি নির্দেশাবলী পাইপলাইনে যেতে পারে।

শাখা ভবিষ্যদ্বাণী ছাড়া

3 টি নির্দেশাবলী সম্পূর্ণ করতে এটি 9 টি ঘড়ি চক্র গ্রহণ করবে।

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

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

এটি 3 টি নির্দেশাবলী সম্পূর্ণ করতে 7 ঘড়ি চক্র গ্রহণ করবে।

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

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

3 টি নির্দেশাবলী সম্পূর্ণ করতে এটি 9 টি ঘড়ি চক্র গ্রহণ করবে।

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

আপনি দেখতে পাচ্ছেন, দেখে মনে হচ্ছে আমাদের কাছে ব্রাঞ্চ প্রিডিকটর ব্যবহার না করার কারণ নেই।

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


1
ইন্টেল বিপণন অ্যানিমেশনগুলির মতো প্রায়শই ভাল এবং এগুলি কেবল শাখার পূর্বাভাসের সাথেই নয় বরং অর্ডার এক্সিকিউশনের বাইরে পড়েছিল, উভয় কৌশলই "অনুমানমূলক" ছিল। মেমরি এবং সঞ্চয়স্থানে এগিয়ে পড়া (বাফারে অনুক্রমিক প্রাক-আনয়ন) এছাড়াও অনুমানযোগ্য। এটি সব যোগ করে।
ম্যাকেনজম

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

209

শাখা-পূর্বাভাস লাভ!

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

if (expression)
{
    // Run 1
} else {
    // Run 2
}

যখনই কোনও if-else\ switchবিবৃতি থাকে, কোন ব্লকটি কার্যকর করা উচিত তা নির্ধারণের জন্য অভিব্যক্তিটির মূল্যায়ন করতে হবে। সংকলক দ্বারা উত্পন্ন সমাবেশ কোডে, শর্তাধীন শাখার নির্দেশাবলী সন্নিবেশ করা হয়।

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

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

ভিজুয়ালাইজেশান:

আসুন ধরা যাক আপনার 1 বা রুট 2 বেছে নেওয়া দরকার আপনার সঙ্গীর মানচিত্রটি যাচাই করার জন্য অপেক্ষা করা, আপনি ## এ দাঁড়িয়েছেন এবং অপেক্ষা করেছেন, বা আপনি কেবল রুট 1 বেছে নিতে পারেন এবং যদি আপনি ভাগ্যবান হন (রুট 1 সঠিক রুট), তারপরে দুর্দান্ত আপনার মানচিত্রটি পরীক্ষা করার জন্য আপনার সঙ্গীর অপেক্ষা করতে হবে না (আপনি মানচিত্রটি পরীক্ষা করতে সময় নেওয়ার সময়টি সংরক্ষণ করেছিলেন), অন্যথায় আপনি কেবল ফিরে যাবেন।

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

 O      Route 1  /-------------------------------
/|\             /
 |  ---------##/
/ \            \
                \
        Route 2  \--------------------------------

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

153

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

এই অ্যালগরিদমের অভ্যন্তরীণ লুপটি এআরএম সমাবেশ ভাষায় নিম্নলিখিতগুলির মতো দেখতে লাগবে:

MOV R0, #0     // R0 = sum = 0
MOV R1, #0     // R1 = c = 0
ADR R2, data   // R2 = addr of data array (put this instruction outside outer loop)
.inner_loop    // Inner loop branch label
    LDRB R3, [R2, R1]     // R3 = data[c]
    CMP R3, #128          // compare R3 to 128
    ADDGE R0, R0, R3      // if R3 >= 128, then sum += data[c] -- no branch needed!
    ADD R1, R1, #1        // c++
    CMP R1, #arraySize    // compare c to arraySize
    BLT inner_loop        // Branch to inner_loop if c < arraySize

তবে এটি আসলে আরও বড় ছবির অংশ:

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

শর্ত পরীক্ষার ক্ষেত্র এবং "চ্ছিক "সেট স্ট্যাটাস বিট" ক্ষেত্রটি একত্রিত হতে পারে, উদাহরণস্বরূপ:

  • ADD R1, R2, R3R1 = R2 + R3কোনও স্ট্যাটাস বিট আপডেট না করে সম্পাদন করে ।
  • ADDGE R1, R2, R3 আগের নির্দেশাবলী যা স্থিতির বিটগুলিকে প্রভাবিত করে তার ফলাফল বৃহত্তর বা সমান শর্তের ফলে ঘটতে পারে same
  • ADDS R1, R2, R3সঞ্চালিত উপরন্তু এবং তারপর আপডেট N, Z, Cএবং Vপ্রসেসর স্থিতি নিবন্ধন কিনা ফলাফলের নেতিবাচক, জিরো, কাজ করেছিল (স্বাক্ষরবিহীন উপরন্তু জন্য) ছিল ভিত্তি করে ফ্ল্যাগ, অথবা (স্বাক্ষরিত উপরন্তু জন্য) উদ্বেলিত।
  • ADDSGE R1, R2, R3GEপরীক্ষাটি সত্য হলেই সংযোজনটি সম্পাদন করে এবং তারপরে সংযোজনের ফলাফলের ভিত্তিতে স্থিতির বিটগুলি আপডেট করে।

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

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


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

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

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

বিটিডাব্লু, আপনি অ্যারের শেষের সাথে তুলনা করে লুপে কোনও নির্দেশিকা সংরক্ষণ করতে পারেন। লুপের আগে, সেট আপ করুন R2 = data + arraySize, তারপরে শুরু করুন R1 = -arraySize। লুপের নীচে adds r1, r1, #1/ হয়ে যায় bnz inner_loop। কম্পাইলার কোনো কারণে এই অপ্টিমাইজেশান ব্যবহার করবেন না: / কিন্তু যাই হোক, অ্যাড এর predicated সঞ্চালনের মৌলিকভাবে আপনি এক্স 86 এর মতো অন্যান্য ISAs উপর branchless কোড সহ কি করতে পারি থেকে এই ক্ষেত্রে ভিন্ন নয় cmov। যদিও এটি ততটা সুন্দর নয়: জিসিসি অপ্টিমাইজেশান পতাকা -O3 কোডটি -2
পিটার

1
(এআরএম বাস্তবায়নের পূর্বাভাস দিয়েছে সত্যই নির্দেশটি এনওপিগুলি, যাতে আপনি এটি এমন লোড বা স্টোরগুলিতেও ব্যবহার করতে পারেন যা cmovমেমোরি উত্স অপারেন্ডের সাথে x86 এর বিপরীতে থাকে A AArch64 সহ বেশিরভাগ আইএসএই কেবলমাত্র ALU নির্বাচন পরিচালনা করে থাকে। সুতরাং আর্মের ভবিষ্যদ্বাণী শক্তিশালী হতে পারে, এবং বেশিরভাগ আইএসএতে শাখাবিহীন কোডের চেয়ে আরও দক্ষতার সাথে ব্যবহারযোগ্য))
পিটার

146

এটি শাখার পূর্বাভাস সম্পর্কে। এটা কি?

  • একটি শাখা ভবিষ্যদ্বাণী প্রাচীন কার্য সম্পাদন কৌশলগুলির মধ্যে একটি যা এখনও আধুনিক স্থাপত্যের মধ্যে প্রাসঙ্গিকতা খুঁজে পায়। সাধারণ ভবিষ্যদ্বাণী করার কৌশলগুলি দ্রুত অনুসন্ধান এবং পাওয়ার দক্ষতা সরবরাহ করে তবে তারা একটি উচ্চ ভুলের হার থেকে ভোগে।

  • অন্যদিকে, জটিল শাখার পূর্বাভাস - উভয় স্তরের শাখা পূর্বাভাসের নিউরাল ভিত্তিক বা ভেরিয়েন্টগুলি - আরও ভাল পূর্বাভাসের নির্ভুলতা সরবরাহ করে তবে তারা বেশি শক্তি গ্রহণ করে এবং জটিলতা তাত্পর্যপূর্ণভাবে বৃদ্ধি পায়।

  • এগুলি ছাড়াও, জটিল ভবিষ্যদ্বাণী কৌশলগুলিতে শাখাগুলি পূর্বাভাস দেওয়ার জন্য নেওয়া সময়টি নিজেই খুব বেশি থাকে - 2 থেকে 5 চক্র পর্যন্ত থাকে - যা প্রকৃত শাখাগুলির সম্পাদনের সময়ের সাথে তুলনীয়।

  • শাখার পূর্বাভাস মূলত একটি অপ্টিমাইজেশন (হ্রাসকরণ) সমস্যা যেখানে সর্বনিম্ন সম্ভাব্য মিস রেট, স্বল্প বিদ্যুত ব্যবহার এবং নূন্যতম সংস্থান সহ কম জটিলতা অর্জনের দিকে জোর দেওয়া হয়।

সত্যিই তিনটি বিভিন্ন ধরণের শাখা রয়েছে:

ফরোয়ার্ড শর্তসাপেক্ষ শাখা - একটি রান-টাইম শর্তের ভিত্তিতে, পিসি (প্রোগ্রামের কাউন্টার) পরিবর্তন স্ট্রিমের একটি ঠিকানার দিকে ইঙ্গিত করা হয়।

পশ্চাদপদ শর্তসাপেক্ষ শাখা - পিসিটি নির্দেশের স্ট্রিমের পিছনে পয়েন্টে পরিবর্তিত হয়। শাখাটি কিছু শর্তের উপর ভিত্তি করে তৈরি করা হয় যেমন প্রোগ্রাম লুপের প্রারম্ভের দিকে পিছনে শাখা করা যখন লুপের শেষে টেস্টে বলা হয় যে লুপটি আবার কার্যকর করা উচিত।

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

স্থিতিশীল / গতিশীল শাখার পূর্বাভাস : স্ট্যাটিক শাখার পূর্বাভাস মাইক্রোপ্রসেসর দ্বারা প্রথমবার শর্তযুক্ত শাখার মুখোমুখি হয়েছিল এবং শর্তাধীন শাখা কোড কার্যকর করার জন্য গতিশীল শাখার পূর্বাভাস ব্যবহার করা হয়।

তথ্যসূত্র:


145

শাখার পূর্বাভাস আপনাকে হ্রাস করতে পারে এগুলি ছাড়াও, বাছাই করা অ্যারের আরও একটি সুবিধা রয়েছে:

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

 // sort backwards (higher values first), may be in some other part of the code
 std::sort(data, data + arraySize, std::greater<int>());

 for (unsigned c = 0; c < arraySize; ++c) {
       if (data[c] < 128) {
              break;
       }
       sum += data[c];               
 }

1
ঠিক আছে, তবে অ্যারে বাছাইয়ের সেটআপ ব্যয় হ'ল ও (এন লগ এন), তাই শীঘ্রই ব্রেক করা আপনাকে সহায়তা করে না যদি আপনি অ্যারে বাছাই করার একমাত্র কারণটি দ্রুত ভাঙতে সক্ষম হয়। যদি যাইহোক, অ্যারের প্রাক বাছাই করার অন্যান্য কারণ আপনার কাছে রয়েছে তবে হ্যাঁ, এটি মূল্যবান।
লুক হাচিসন

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

2
হ্যাঁ, আমি আমার প্রথম মন্তব্যে ঠিক এটিই করেছি :-) আপনি বলেছেন "শাখার পূর্বাভাসটি কেবল একবারই মিস হবে miss" তবে আপনি বাছাই করা অ্যালগরিদমের অভ্যন্তরে ও (এন লগ এন) শাখার পূর্বাভাস মিস করে না, যা অমীমাংসিত ক্ষেত্রে ও (এন) শাখার পূর্বাভাস মিস করার চেয়ে বেশি। সুতরাং আপনাকে বাছাই করার জন্য বাছাই করা তথ্য ও (লগ এন) বারের সম্পূর্ণতা ব্যবহার করতে হবে (সম্ভবত আসলে ও (10 লগ এন) এর কাছাকাছি, বাছাই করা অ্যালগরিদমের উপর নির্ভর করে, যেমন কুইকোর্টের জন্য, ক্যাশে মিস করার কারণে - সংযুক্তি আরও ক্যাশে-সুসংহত, সুতরাং আপনার আরও ঘনিষ্ঠ হওয়া দরকার (2 লগ এন) এমনকি বিরতিতে ব্যবহার করতে হবে))
লুক হাচিসন

তবে একটি উল্লেখযোগ্য অপ্টিমাইজেশন কেবলমাত্র "অর্ধেক কুইকোর্ট" করতে হবে, কেবলমাত্র 127 এর লক্ষ্য পিভট মানের চেয়ে কম আইটেমকে বাছাই করা (পিভটের চেয়ে কম বা সমান কিছু ধরে নেওয়া )) একবার আপনি পাইভটে পৌঁছানোর পরে, পাইভটের আগে উপাদানগুলি যোগ করুন। এটি ও (এন) প্রারম্ভকালে ও (এন লগ এন) এর পরিবর্তে চলবে, যদিও এখনও অনেকগুলি শাখা পূর্বাভাস মিস করবে, সম্ভবত আমি আগে যে সংখ্যাগুলি দিয়েছি তার উপর ভিত্তি করে ও (5 এন) এর ক্রমটি পেয়েছে এটি অর্ধেক কুইস্কোর্ট।
লুক হাচিসন

132

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

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

একটি ভুল ভবিষ্যদ্বাণী করা পূর্ববর্তী পদক্ষেপে ফিরে যেতে এবং অন্য একটি ভবিষ্যদ্বাণী দিয়ে সম্পাদন করে। ভবিষ্যদ্বাণীটি সঠিক বলে ধরে নিলে কোডটি পরবর্তী ধাপে চালিয়ে যাবে। একটি ভুল পূর্বাভাসের ফলাফল একই ধাপটি পুনরাবৃত্তি করে, যতক্ষণ না একটি সঠিক ভবিষ্যদ্বাণী ঘটে।

আপনার প্রশ্নের উত্তর খুব সহজ।

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

সাজানো অ্যারে: স্ট্রেইট রোড ____________________________________________________________________________________________ - - - - - - - - - - - - - - - - - - - - - টিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটিটি টিটিটিটি টিটিটিটিটি

অরসোর্টড অ্যারে: বাঁকা রোড

______   ________
|     |__|

শাখার পূর্বাভাস: কোন রাস্তাটি সোজা তা অনুমান করা / ভবিষ্যদ্বাণী করা এবং চেক না করেই এটি অনুসরণ করা

___________________________________________ Straight road
 |_________________________________________|Longer road

যদিও উভয় রাস্তাই একই গন্তব্যে পৌঁছেছে, সরল রাস্তাটি ছোট এবং অন্যটি দীর্ঘ। যদি আপনি ভুল করে অন্যটিকে চয়ন করেন তবে কোনও পিছনে ফিরে আসবে না, এবং যদি আপনি দীর্ঘ রাস্তাটি বেছে নেন তবে আপনি কিছু অতিরিক্ত সময় নষ্ট করবেন। এটি কম্পিউটারে যা ঘটে তার অনুরূপ এবং আমি আশা করি এটি আপনাকে আরও ভালভাবে বুঝতে সহায়তা করেছে।


এছাড়াও আমি মন্তব্যগুলি থেকে @ সিমন_উইভারটি উদ্ধৃত করতে চাই :

এটি কম পূর্বাভাস দেয় না - এটি কম ভুল পূর্বাভাস দেয়। এটি এখনও প্রতিটি সময়ের জন্য লুপের মাধ্যমে ভবিষ্যদ্বাণী করতে হবে ...


122

আমি নিম্নলিখিত ম্যাটল্যাব কোডের জন্য আমার ম্যাকবুক প্রো (ইন্টেল আই 7, 64 বিট, 2.4 গিগাহার্টজ) এর সাথে এমএটিএলবি 2011 বিতে একই কোডটি চেষ্টা করেছি:

% Processing time with Sorted data vs unsorted data
%==========================================================================
% Generate data
arraySize = 32768
sum = 0;
% Generate random integer data from range 0 to 255
data = randi(256, arraySize, 1);


%Sort the data
data1= sort(data); % data1= data  when no sorting done


%Start a stopwatch timer to measure the execution time
tic;

for i=1:100000

    for j=1:arraySize

        if data1(j)>=128
            sum=sum + data1(j);
        end
    end
end

toc;

ExeTimeWithSorting = toc - tic;

উপরের ম্যাটল্যাব কোডের ফলাফলগুলি নিম্নরূপ:

  a: Elapsed time (without sorting) = 3479.880861 seconds.
  b: Elapsed time (with sorting ) = 2377.873098 seconds.

@GManNickG হিসাবে সি কোডের ফলাফল আমি পেয়েছি:

  a: Elapsed time (without sorting) = 19.8761 sec.
  b: Elapsed time (with sorting ) = 7.37778 sec.

এর উপর ভিত্তি করে, দেখে মনে হচ্ছে ম্যাটল্যাব সি বাছাই ছাড়াই সি বাস্তবায়নের চেয়ে প্রায় 175 গুণ ধীরে ধীরে এবং বাছাইয়ের সাথে 350 গুণ ধীর। অন্য কথায়, প্রভাব (শাখার পূর্বাভাসের) এমএটিএলবি বাস্তবায়নের জন্য 1.46x এবং সি বাস্তবায়নের জন্য 2.7x


6
কেবলমাত্র সম্পূর্ণতার জন্য, আপনি সম্ভবত এটি মাতলাবকে বাস্তবায়ন করবেন না। আমি বাজি দিয়েছি যে সমস্যাটি ভেক্টরাইজ করার পরে যদি করা হয় তবে এটি আরও দ্রুত হবে।
ysap

1
মতলব অনেক পরিস্থিতিতে স্বয়ংক্রিয় সমান্তরালকরণ / ভেক্টরাইজেশন করে তবে এখানে সমস্যাটি শাখার পূর্বাভাসের প্রভাব পরীক্ষা করে। মতলব কোনওভাবেই অনাক্রম্য নয়!
শান

1
মতলব কী নেটিভ নম্বর বা একটি মাদুর ল্যাব নির্দিষ্ট প্রয়োগকরণ (অঙ্কের অসীম পরিমাণ বা তাই?) ব্যবহার করে
থরবজর্ন রাভন অ্যান্ডারসেন

54

অন্যান্য উত্তর দ্বারা অনুমান যে এক একটি তথ্য বাছাই করা প্রয়োজন সঠিক নয়।

নিম্নলিখিত কোডটি পুরো অ্যারেটিকে বাছাই করে না, তবে এটির মধ্যে কেবল 200-উপাদান উপাদানগুলি রয়েছে এবং এর ফলে দ্রুততম সঞ্চালিত হয়।

কেবলমাত্র-এলিমেন্ট বিভাগগুলি বাছাই করা সম্পূর্ণ অ্যারেটিকে সাজানোর জন্য প্রয়োজনীয় সময়ের O(n)চেয়ে লিনিয়ার সময়ে প্রাক প্রসেসিং সম্পূর্ণ করে O(n.log(n))

#include <algorithm>
#include <ctime>
#include <iostream>

int main() {
    int data[32768]; const int l = sizeof data / sizeof data[0];

    for (unsigned c = 0; c < l; ++c)
        data[c] = std::rand() % 256;

    // sort 200-element segments, not the whole array
    for (unsigned c = 0; c + 200 <= l; c += 200)
        std::sort(&data[c], &data[c + 200]);

    clock_t start = clock();
    long long sum = 0;

    for (unsigned i = 0; i < 100000; ++i) {
        for (unsigned c = 0; c < sizeof data / sizeof(int); ++c) {
            if (data[c] >= 128)
                sum += data[c];
        }
    }

    std::cout << static_cast<double>(clock() - start) / CLOCKS_PER_SEC << std::endl;
    std::cout << "sum = " << sum << std::endl;
}

এটি "প্রমাণিত "ও করে যে এর কোনও অ্যালগরিদমিক ইস্যু যেমন বাছাইয়ের ক্রমের সাথে কোনও সম্পর্ক নেই এবং এটি প্রকৃতপক্ষে শাখার পূর্বাভাস।


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

যদি আপনি কেবল অচিরাচরিত তথ্যের চেয়ে দক্ষতার সাথে অ্যালগরিদমটি প্রয়োগ করতে চান তবে আপনি শাখাবিহীনভাবে এই অপারেশনটি করবেন (এবং সিমডি সহ, উদাহরণস্বরূপ x86 pcmpgtbদিয়ে তাদের উচ্চ বিট সেট সহ উপাদানগুলি সন্ধান করুন এবং তারপরে ছোট ছোট উপাদানগুলিকে শূন্য করুন)। আসলে বাছাইয়ের খণ্ডগুলি যে কোনও সময় ব্যয় করা ধীর হবে be একটি শাখাবিহীন সংস্করণে ডেটা-স্বতন্ত্র পারফরম্যান্স থাকবে, এটি প্রমাণ করে যে ব্যয়টি শাখার ভুল ধারণা থেকে এসেছে। অথবা শুধু ব্যবহার কর্মক্ষমতা কাউন্টারে সরাসরি পর্যবেক্ষণ যে, Skylake মত int_misc.clear_resteer_cyclesবা int_misc.recovery_cyclesmispredicts থেকে ফ্রন্ট-এন্ড অলস চক্র গণনা
পিটার Cordes

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

36

এই প্রশ্নের উত্তরে বারজন স্ট্রস্ট্রপের উত্তর :

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

সুতরাং, আমি এক মিলিয়ন পূর্ণসংখ্যার ভেক্টর দিয়ে চেষ্টা করেছি এবং পেয়েছি:

Already sorted    32995 milliseconds
Shuffled          125944 milliseconds

Already sorted    18610 milliseconds
Shuffled          133304 milliseconds

Already sorted    17942 milliseconds
Shuffled          107858 milliseconds

আমি নিশ্চিত হয়ে কয়েকবার দৌড়েছি। হ্যাঁ, ঘটনাটি বাস্তব। আমার মূল কোডটি ছিল:

void run(vector<int>& v, const string& label)
{
    auto t0 = system_clock::now();
    sort(v.begin(), v.end());
    auto t1 = system_clock::now();
    cout << label 
         << duration_cast<microseconds>(t1  t0).count() 
         << " milliseconds\n";
}

void tst()
{
    vector<int> v(1'000'000);
    iota(v.begin(), v.end(), 0);
    run(v, "already sorted ");
    std::shuffle(v.begin(), v.end(), std::mt19937{ std::random_device{}() });
    run(v, "shuffled    ");
}

এই সংকলক, স্ট্যান্ডার্ড লাইব্রেরি এবং অপ্টিমাইজার সেটিংসের সাথে অন্ততপক্ষে ঘটনাটি বাস্তব। বিভিন্ন বাস্তবায়ন বিভিন্ন উত্তর দিতে পারে এবং করতে পারে। প্রকৃতপক্ষে, কেউ একটি আরও নিয়মতান্ত্রিক অধ্যয়ন করেছেন (একটি দ্রুত ওয়েব অনুসন্ধান এটি সন্ধান করবে) এবং বেশিরভাগ বাস্তবায়ন সেই প্রভাবটি দেখায়।

একটি কারণ শাখা পূর্বাভাস: বাছাই অ্যালগরিদমের মূল অপারেশন হ'ল “if(v[i] < pivot]) …”বা সমতুল্য। বাছাই করা ক্রমের জন্য যে পরীক্ষাটি সর্বদা সত্য, তবে একটি এলোমেলো অনুক্রমের জন্য, নির্বাচিত শাখাটি এলোমেলোভাবে পরিবর্তিত হয়।

আর একটি কারণ হ'ল যখন ভেক্টরটি ইতিমধ্যে বাছাই করা হয় তখন আমাদের কখনই উপাদানগুলিকে তাদের সঠিক অবস্থানে স্থানান্তর করতে হবে না। এই ছোট্ট বিশদটির প্রভাবটি আমরা দেখেছি পাঁচ বা ছয়টির ফ্যাক্টর।

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

আপনি যদি দক্ষ কোড লিখতে চান তবে আপনাকে মেশিন আর্কিটেকচার সম্পর্কে কিছুটা জানতে হবে।


27

এই প্রশ্নটি মূলত সিপিইউগুলিতে শাখা প্রেডিকশন মডেলগুলিতে রয়েছে। আমি এই কাগজ পড়ার সুপারিশ করব:

একাধিক শাখার পূর্বাভাস এবং একটি শাখার ঠিকানা ক্যাশে মাধ্যমে নির্দেশিকা আনার হার বৃদ্ধি করা

যখন আপনি উপাদানগুলি বাছাই করেছেন, আইআর সমস্ত সিপিইউ নির্দেশনা বারবার বার বার নিতে বিরক্ত হতে পারে না, এটি তাদের ক্যাশে থেকে আনে।


সিপিইউর এল 1 নির্দেশের ক্যাশে ভুল ত্রুটিবিহীনভাবে নির্দেশাবলী গরম থাকে। তাত্ক্ষণিক-পূর্ববর্তী নির্দেশাবলিগুলি ডিকোড করে কার্যকর করা শেষ হওয়ার আগে সমস্যাটি তাদের সঠিক ক্রমে পাইপলাইনে আনছে ।
পিটার

15

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

তবে এই ক্ষেত্রে, আমরা জানি মানগুলি [0, 255] এর মধ্যে থাকে এবং আমরা কেবল মানগুলি> = 128 কেই যত্নশীল করি That এর অর্থ আমরা সহজেই একটি বিট বের করতে পারি যা আমাদের মান চায় কিনা তা আমাদের বলবে: স্থানান্তরিত করে ডান b বিটের ডেটা, আমরা একটি 0 বিট বা 1 বিট দিয়ে রেখেছি এবং আমরা যখন 1 বিট থাকি তখনই আমরা মানটি যুক্ত করতে চাই। এই বিটটিকে "সিদ্ধান্ত বিট" বলি।

অ্যারেতে সূচি হিসাবে সিদ্ধান্ত বিটের 0/1 মান ব্যবহার করে আমরা কোড তৈরি করতে পারি যা ডেটা বাছাই করা হয় বা না সাজানো হয় তা সমান দ্রুত হবে। আমাদের কোড সর্বদা একটি মান যুক্ত করবে, কিন্তু যখন সিদ্ধান্ত বিট 0 হয়, আমরা সেই জায়গাতেই আমাদের মূল্যবোধ করব না। কোডটি এখানে:

// পরীক্ষা

clock_t start = clock();
long long a[] = {0, 0};
long long sum;

for (unsigned i = 0; i < 100000; ++i)
{
    // Primary loop
    for (unsigned c = 0; c < arraySize; ++c)
    {
        int j = (data[c] >> 7);
        a[j] += data[c];
    }
}

double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;
sum = a[1];

এই কোডটি অ্যাডগুলির অর্ধেক অপচয় করে তবে কোনও শাখার পূর্বাভাস ব্যর্থ হয় না। এটি সত্যিকারের if স্টেটমেন্ট সহ ভার্সনের তুলনায় এলোমেলো ডেটাতে অত্যন্ত দ্রুত।

তবে আমার পরীক্ষায়, একটি স্পষ্টতূপে অনুসন্ধানের টেবিলটি এর চেয়ে কিছুটা দ্রুত ছিল সম্ভবত কারণ দেখার জন্য সারণিতে সূচিটি কিছুটা স্থানান্তরিত করার চেয়ে কিছুটা দ্রুত ছিল। এটি দেখায় যে কীভাবে আমার কোডটি সেট আপ করে এবং লুকিং টেবিলটি ব্যবহার করে (কোডটিতে "লুকআপ টেবিল" এর জন্য অকল্পনীয়ভাবে লট বলা হয়)। এখানে সি ++ কোডটি রয়েছে:

// ঘোষণা করুন এবং তারপরে লুকিং টেবিলটি পূরণ করুন

int lut[256];
for (unsigned c = 0; c < 256; ++c)
    lut[c] = (c >= 128) ? c : 0;

// Use the lookup table after it is built
for (unsigned i = 0; i < 100000; ++i)
{
    // Primary loop
    for (unsigned c = 0; c < arraySize; ++c)
    {
        sum += lut[data[c]];
    }
}

এই ক্ষেত্রে, সন্ধানের টেবিলটি কেবল 256 বাইট ছিল, সুতরাং এটি একটি ক্যাশে চমৎকারভাবে ফিট করে এবং সমস্ত দ্রুত ছিল। যদি ডেটা 24-বিট মান হয় এবং এই কৌশলটি কার্যকর হবে না এবং আমরা কেবল তার অর্ধেক চেয়েছিলাম ... দেখার জন্য টেবিলটি ব্যবহারিক হওয়ার চেয়ে অনেক বড় হবে। অন্যদিকে, আমরা উপরে প্রদর্শিত দুটি কৌশল একত্রিত করতে পারি: প্রথমে বিটগুলি স্থানান্তরিত করুন, তারপরে একটি সন্ধানের টেবিলটি সূচী করুন। আমরা কেবলমাত্র শীর্ষ অর্ধেক মান চাই এমন একটি 24-বিট মানের জন্য, আমরা 12 বিট দ্বারা সম্ভাব্যভাবে ডানটি স্থানান্তর করতে পারি এবং টেবিল সূচকের জন্য 12-বিট মান রেখে যেতে পারি। একটি 12-বিট টেবিল সূচক 4096 মানগুলির একটি সারণিকে বোঝায় যা ব্যবহারিক হতে পারে।

কোনও অ্যারেতে ইনডেক্স করার কৌশলটি যদি একটি আইএফ স্টেটমেন্ট ব্যবহার না করে কোন পয়েন্টারটি ব্যবহার করে তা সিদ্ধান্ত নেওয়ার জন্য ব্যবহার করা যেতে পারে। আমি একটি লাইব্রেরি দেখেছি যা বাইনারি গাছ প্রয়োগ করেছে এবং তার পরিবর্তে দুটি নামক পয়েন্টার (পি লেফট এবং পিআরাইট বা যাই হোক না কেন) পয়েন্টারগুলির দৈর্ঘ্য -2 অ্যারে ছিল এবং কোনটি অনুসরণ করা উচিত তা সিদ্ধান্ত নিতে "সিদ্ধান্ত বিট" কৌশলটি ব্যবহার করে। উদাহরণস্বরূপ, পরিবর্তে:

if (x < node->value)
    node = node->pLeft;
else
    node = node->pRight;
this library would do something like:

i = (x < node->value);
node = node->link[i];

এটি একটি দুর্দান্ত সমাধান সম্ভবত এটি কাজ করবে


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

একটি 4096 এন্ট্রি সন্ধানের টেবিলটি উন্মাদ শোনায়। আপনি যদি কোনও বিট স্থানান্তরিত করেন তবে আপনি মূল নম্বরটি যুক্ত করতে চাইলে কেবলমাত্র LUT ফলাফল ব্যবহার করতে পারবেন না । আপনার সংকলকটি সহজে শাখাবিহীন কৌশল ব্যবহার না করে কাজ করার জন্য মূর্খ কৌশলগুলির মতো এই সমস্ত শব্দ। আরও সোজা হবে mask = tmp < 128 : 0 : -1UL;/total += tmp & mask;
পিটার কর্ডেস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.