কোনও পূর্ণসংখ্যা দুটি মানগুলির সমাহার সহ দুটি সংখ্যার (সমেত) মধ্যে হয় কিনা তা নির্ধারণের দ্রুততম উপায়


389

x >= start && x <= endকোনও পূর্ণসংখ্যা দুটি পূর্ণসংখ্যার মধ্যে হয় কিনা তা পরীক্ষা করার জন্য সি বা সি ++ এর চেয়ে আরও দ্রুততর উপায় আছে কি ?

হালনাগাদ : আমার নির্দিষ্ট প্ল্যাটফর্মটি আইওএস। এটি একটি বক্স ব্লার ফাংশনের একটি অংশ যা প্রদত্ত স্কোয়ারের একটি বৃত্তে পিক্সেলকে সীমাবদ্ধ করে।

আপডেট : গ্রহণযোগ্য উত্তর চেষ্টা করার পরে , আমি স্বাভাবিকের চেয়ে এক লাইনের কোডে প্রস্থের গতি সম্পন্ন করার আদেশ পেয়েছিx >= start && x <= end

হালনাগাদ : এক্সকোড থেকে এসেম্বলারের সাথে কোডটি পরে এবং আগে এখানে রয়েছে:

নতুন উপায়

// diff = (end - start) + 1
#define POINT_IN_RANGE_AND_INCREMENT(p, range) ((p++ - range.start) < range.diff)

Ltmp1313:
 ldr    r0, [sp, #176] @ 4-byte Reload
 ldr    r1, [sp, #164] @ 4-byte Reload
 ldr    r0, [r0]
 ldr    r1, [r1]
 sub.w  r0, r9, r0
 cmp    r0, r1
 blo    LBB44_30

পুরানো উপায়

#define POINT_IN_RANGE_AND_INCREMENT(p, range) (p <= range.end && p++ >= range.start)

Ltmp1301:
 ldr    r1, [sp, #172] @ 4-byte Reload
 ldr    r1, [r1]
 cmp    r0, r1
 bls    LBB44_32
 mov    r6, r0
 b      LBB44_33
LBB44_32:
 ldr    r1, [sp, #188] @ 4-byte Reload
 adds   r6, r0, #1
Ltmp1302:
 ldr    r1, [r1]
 cmp    r0, r1
 bhs    LBB44_36

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


28
আপনি কেন উদ্বিগ্ন যে এটি আপনার পক্ষে যথেষ্ট দ্রুত নয়?
ম্যাট বল

90
কে কেন যত্ন করে, এটি একটি আকর্ষণীয় প্রশ্ন। এটি একটি চ্যালেঞ্জের স্বার্থে কেবল একটি চ্যালেঞ্জ।
ডেভিড মনিকাকে

46
@ এসএলাকস সুতরাং আমাদের এই জাতীয় প্রশ্নগুলি অন্ধভাবে উপেক্ষা করা এবং কেবল "অপটিমাইজারটি এটি করা যাক?"
ডেভিড মনিকাকে

87
প্রশ্নটি কেন জিজ্ঞাসা করা হচ্ছে তা বিবেচ্য নয়। এটি একটি বৈধ প্রশ্ন, এমনকি যদি উত্তরটি না হয়
tay10r

41
এটি আমার অ্যাপ্লিকেশনগুলির একটিতে একটি ফাংশনের একটি বাধা
jjxtra

উত্তর:


527

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

// use a < for an inclusive lower bound and exclusive upper bound
// use <= for an inclusive lower bound and inclusive upper bound
// alternatively, if the upper bound is inclusive and you can pre-calculate
//  upper-lower, simply add + 1 to upper-lower and use the < operator.
    if ((unsigned)(number-lower) <= (upper-lower))
        in_range(number);

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

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

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

বাস্তবে এই পদ্ধতি অনুবাদ numberএবং উৎপত্তি ও চেক বিন্দু বিরতি যদি numberব্যবধান রয়েছে [0, D], যেখানে D = upper - lower। তাহলে numberনিচের নিম্নতর বাউন্ড: নেতিবাচক , এবং যদি উপরের উপরে আবদ্ধ: চেয়ে বড়D


8
@ টমসবাডান: তারা উভয়ই যে কোনও যুক্তিসঙ্গত মেশিনে একটি চক্র হবে। ব্যয়বহুল কি শাখা হয়।
অলিভার চার্লসওয়ার্থ

3
শর্ট সার্কিটের কারণে অতিরিক্ত ব্রাঞ্চিং করা হয়? যদি এটি হয় তবে কি lower <= x & x <= upper(পরিবর্তে lower <= x && x <= upper) আরও ভাল পারফরম্যান্সের ফলাফল হবে?
মার্কাস মেয়ার

6
@ একে ৪৪৪৯, জেএক্সএইচ: এই নটটি যতটা শীতল, আমি উত্সাহ দিতে দ্বিধাগ্রস্ত, কারণ দুর্ভাগ্যক্রমে এটিকে বাস্তবে দ্রুততর করার পরামর্শ দেওয়ার মতো কিছুই নেই (যতক্ষণ না কেউ ফলাফল সংঘবদ্ধ এবং প্রোফাইলিং তথ্যের তুলনা করে) does আমরা যা জানি, ওপির সংকলক ওপির কোডটি একটি একক শাখা অপকোড দিয়ে রেন্ডার করতে পারে ...
অলিভার চার্লসওয়ার্থ

152
কি দারুন!!! এর ফলে কোডটির এই নির্দিষ্ট লাইনটির জন্য আমার অ্যাপ্লিকেশনে প্রস্থের উন্নতির ক্রম হয়েছিল। আপার-লোয়ার প্রাক্কুট করে আমার প্রোফাইলিং এই ফাংশনের 25% সময় থেকে 2% এরও কম চলে গেল! বোতল নেক এখন সংযোজন এবং বিয়োগ অপারেশন, তবে আমি মনে করি এটি এখন যথেষ্ট ভাল হতে পারে :)
jjxtra

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

17

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


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

17

এটি একই ডেটা দিয়ে আপনি কতবার পরীক্ষা করতে চান তার উপর নির্ভর করে।

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

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

আপনার ডেটা জন্য অনুসন্ধান সারণী 128 ^ 3 = 2,097,152 হবে। যদি আপনি তিনটি ভেরিয়েবলের মধ্যে একটি নিয়ন্ত্রণ করতে পারেন তবে আপনি সমস্ত দৃষ্টান্ত বিবেচনা করুন যেখানে start = Nএক সময়, তখন কার্যকরী সেটটির আকারটি 128^2 = 16432বাইটে নেমে যায় , যা বেশিরভাগ আধুনিক ক্যাশে ভালভাবে ফিট করা উচিত।

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


সুতরাং আপনি কোনও মান, শুরু এবং শেষের দিক দিয়ে কিছু ধরণের লুকোচুরি সঞ্চয় করবেন এবং এতে কোনও বিওওএল থাকবে যা আপনাকে বলছে যে এটির মধ্যে ছিল কিনা?
jjxtra

সঠিক। এটা একটা 3D লুকআপ টেবিল হতে হবে: bool between[start][end][x]। আপনি যদি জানেন যে আপনার অ্যাক্সেস প্যাটার্নটি কেমন দেখাচ্ছে (উদাহরণস্বরূপ একঘেয়েমি বৃদ্ধি পাচ্ছে) আপনি পুরো টেবিলটি মেমরির সাথে খাপ খায় না এমনও আপনি স্থানীয়তা সংরক্ষণের জন্য টেবিলটি ডিজাইন করতে পারেন।
অ্যান্ড্রু প্রক

আমি এই পদ্ধতিটি চেষ্টা করে দেখতে এবং এটি কীভাবে যায় তা দেখতে পাচ্ছি কিনা তা আমি দেখতে পাব। আমি প্রতি লাইনে কিছুটা ভেক্টর দিয়ে এটি করার পরিকল্পনা করছি যেখানে পয়েন্টটি বৃত্তের মধ্যে থাকলে বিট সেট হবে। কী ভাবেন যে বাইট বা ইনট 32 বনাম বিট মাস্কিংয়ের চেয়ে দ্রুত হবে?
jjxtra

2

এই উত্তরটি স্বীকৃত উত্তরের সাথে সম্পন্ন একটি পরীক্ষার প্রতিবেদন করা। আমি বাছাই করা এলোমেলো পূর্ণসংখ্যার একটি বৃহত্তর ভেক্টরের উপর একটি বদ্ধ পরিসীমা পরীক্ষা করেছিলাম এবং অবাক করে দিয়েছি (নিম্ন <= num && num <= উচ্চ) এর প্রাথমিক পদ্ধতিটি উপরের গৃহীত উত্তরের চেয়ে দ্রুততর! এইচপি প্যাভিলিয়ন জি 6 (এএমডি এ 6-3400APU 6 জিবি র‌্যাম সহ পরীক্ষা করা হয়েছিল testing পরীক্ষার জন্য মূল কোডটি এখানে ব্যবহার করা হয়েছে:

int num = rand();  // num to compare in consecutive ranges.
chrono::time_point<chrono::system_clock> start, end;
auto start = chrono::system_clock::now();

int inBetween1{ 0 };
for (int i = 1; i < MaxNum; ++i)
{
    if (randVec[i - 1] <= num && num <= randVec[i])
        ++inBetween1;
}
auto end = chrono::system_clock::now();
chrono::duration<double> elapsed_s1 = end - start;

নীচের সাথে তুলনা করুন যা উপরে গৃহীত উত্তর:

int inBetween2{ 0 };
for (int i = 1; i < MaxNum; ++i)
{
    if (static_cast<unsigned>(num - randVec[i - 1]) <= (randVec[i] - randVec[i - 1]))
        ++inBetween2;
}

মনোযোগ দিন যে র‌্যান্ডভেক একটি সাজানো ভেক্টর। ম্যাক্সনামের যে কোনও আকারের জন্য প্রথম পদ্ধতিটি আমার মেশিনে দ্বিতীয়টিকে প্রহার করে!


1
আমার ডেটা বাছাই করা হয়নি এবং আমার পরীক্ষাগুলি আইফোন আর্ম সিপিইউতে রয়েছে। বিভিন্ন ডেটা এবং সিপিইউ সহ আপনার ফলাফলগুলি পৃথক হতে পারে।
jjxtra

আমার পরীক্ষায় বাছাই করা কেবলমাত্র উচ্চ সীমাটি নিম্ন সীমা চেয়ে ছোট নয় তা নিশ্চিত করার জন্য হয়েছিল।
রেজেলি

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

0

যে কোনও পরিবর্তনশীল পরিসীমা পরীক্ষা করার জন্য:

if (x >= minx && x <= maxx) ...

বিট অপারেশন ব্যবহার করা এটি দ্রুত:

if ( ((x - minx) | (maxx - x)) >= 0) ...

এটি একটিতে দুটি শাখা হ্রাস করবে।

আপনি যদি সুরক্ষিত টাইপ সম্পর্কে যত্নশীল হন:

if ((int32_t)(((uint32_t)x - (uint32_t)minx) | ((uint32_t)maxx - (uint32_t)x)) > = 0) ...

আপনি আরও পরিবর্তনশীল পরিসীমা চেক একসাথে একত্রিত করতে পারেন:

if (( (x - minx) | (maxx - x) | (y - miny) | (maxy - y) ) >= 0) ...

এটি 4 টি শাখা হ্রাস করবে 1 এ।

এটি জিসিসিতে পুরানোের চেয়ে 3.4 গুণ বেশি দ্রুত :

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


-4

কেবলমাত্র পূর্ণসংখ্যার উপর কিছুটা চালনা করা সম্ভব নয়?

যেহেতু এটি 0 থেকে 128 এর মধ্যে হতে হবে, অষ্টম বিট সেট করা থাকলে (2 ^ 7) এটি 128 বা তারও বেশি। প্রান্তের ক্ষেত্রে একটি ব্যথা হবে যদিও আপনি অন্তর্ভুক্তি তুলনা করতে চান।


3
সে জানতে চায় x <= end, কোথায় আছে end <= 128। না x <= 128
বেন ভয়েগট

1
এই বিবৃতি " যেহেতু এটি 0 থেকে 128 এর মধ্যে হতে হবে, অষ্টম বিট সেট করা থাকলে (2 ^ 7) এটি 128 বা তারও বেশি " ভুল। 256 বিবেচনা করুন
হ্যাপি গ্রিন কিড নেপস

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