সেট করা সর্বনিম্ন উল্লেখযোগ্য বিটের অবস্থান


120

আমি একটি পূর্ণসংখ্যায় সেট করা সর্বনিম্ন উল্লেখযোগ্য বিটের অবস্থান নির্ধারণের জন্য একটি কার্যকর উপায় সন্ধান করছি, যেমন 0x0FF0 এর জন্য এটি 4 হবে।

একটি তুচ্ছ বাস্তবায়ন হ'ল:

unsigned GetLowestBitPos(unsigned value)
{
   assert(value != 0); // handled separately

   unsigned pos = 0;
   while (!(value & 1))
   {
      value >>= 1;
      ++pos;
   }
   return pos;
}

কোনও ধারণা কীভাবে এটি থেকে কিছু চক্র আটকানো যায়?

(দ্রষ্টব্য: এই প্রশ্নটি এমন লোকদের জন্য যারা এই জাতীয় জিনিসগুলি উপভোগ করেন, লোকেরা আমাকে জাইজোপটিমাইজেশনটি মন্দ বলে না)

[সম্পাদনা] ধারণার জন্য সবাইকে ধন্যবাদ! আমি আরও কয়েকটি জিনিস শিখেছি। শান্ত!


যখন ((মান _ এন >> (++ পোস্ট))! = 0);
টমাস

উত্তর:


170

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

unsigned int v;  // find the number of trailing zeros in 32-bit v 
int r;           // result goes here
static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
  31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];

সহায়ক তথ্যসূত্র:


18
ডাউনটা কেন? এটি সম্ভবত গতির গতির উপর নির্ভর করে দ্রুততম বাস্তবায়ন। এটি অবশ্যই কোড কমপ্যাক্ট, এবং (v & -v) কৌশলটি এমন কিছু যা প্রত্যেককে শিখতে এবং মনে রাখা উচিত।
অ্যাডাম ডেভিস

2
+1 খুব শীতল, যদি কোনও (এক্সএন্ডওয়াই) অপারেশনটির সাথে তুলনা করা যায় তবে একাধিক অপারেশন কত ব্যয়বহুল?
ব্রায়ান আর বন্ডি 18

4
কেহ জানেন কিভাবে এই কর্মক্ষমতা তুলনা করে __builtin_ffslবা ffsl?
স্টিভেন লু

2
@ জিম বাল্টার, তবে আধুনিক হার্ডওয়্যারে গুণনের তুলনায় মডুলো খুব ধীর। সুতরাং আমি এটিকে আরও ভাল সমাধান বলব না।
এপ্রিওরি

2
এটি আমার কাছে মনে হচ্ছে 0x01 এবং 0x00 উভয়ই অ্যারে থেকে 0 টির মান দেয়। স্পষ্টতই এই কৌশলটি নির্দেশ করবে যে 0 টি পাস করা হলে সর্বনিম্ন বিট সেট করা আছে!
Abelenky

80

বিল্ট-ইন এফএফএস ব্যবহার করবেন না কেন ? (আমি লিনাক্স থেকে একটি ম্যান পৃষ্ঠা পেয়েছি তবে এটি এর চেয়ে আরও ব্যাপকভাবে উপলব্ধ))

ffs (3) - লিনাক্স ম্যান পৃষ্ঠা

নাম

ffs - কোনও শব্দে প্রথম বিট সেট করুন

সংক্ষিপ্তসার

#include <strings.h>
int ffs(int i);
#define _GNU_SOURCE
#include <string.h>
int ffsl(long int i);
int ffsll(long long int i);

বিবরণ

Ffs () ফাংশনটি i শব্দের মধ্যে প্রথম (অন্তত গুরুত্বপূর্ণ) বিটের সেটটি প্রদান করে। সর্বনিম্ন উল্লেখযোগ্য বিট হ'ল অবস্থান 1 এবং সর্বাধিক তাৎপর্যপূর্ণ অবস্থান যেমন 32 অথবা 64 The

ফেরত মূল্য

এই ফাংশনগুলি প্রথম বিটের সেটটির অবস্থানটি ফিরিয়ে দেয়, বা যদি কোনও বিট সেট করা না থাকে তবে 0।

অনুসারে

4.3BSD, POSIX.1-2001।

মন্তব্য

বিএসডি সিস্টেমগুলির একটি প্রোটোটাইপ রয়েছে <string.h>


6
এফওয়াইআই, এটি উপলভ্য হলে সংশ্লিষ্ট সমাবেশ কমান্ডে সংকলিত হয়।
জের্মি

46

একটি x86 সমাবেশ নির্দেশ রয়েছে (এটি bsf) এটি করবে। :)

আরও অপ্টিমাইজড ?!

সাইড নোট:

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


20
@ ডাব্লুসি: আমি বুঝতে পেরেছি, তবে আমি এই অনুচ্ছেদটি মনে করি: "কোনও ধারণা কীভাবে এটি থেকে কিছু চক্র আটকানো যায়?" যেমন একটি উত্তর পুরোপুরি গ্রহণযোগ্য করে তোলে!
মেহরদাদ আফশারি

5
+1 তাঁর উত্তরটি মূলত শেষের দিকের কারণে তাঁর স্থাপত্যের উপর নির্ভরশীল, সুতরাং সমাবেশের নির্দেশাবলীর দিকে নামানো একটি পুরোপুরি বৈধ উত্তর।
ক্রিস লুটজ

3
+1 চতুর উত্তর, হ্যাঁ এটি সি বা সি ++ নয় তবে কাজের জন্য এটি সঠিক সরঞ্জাম।
অ্যান্ড্রু হার

1
অপেক্ষা করুন, কিছু নয়। পূর্ণসংখ্যার আসল মান এখানে গুরুত্বপূর্ণ নয়। দুঃখিত।
ক্রিস লুটজ

2
@ বাস্টিয়ান: অপারেন্ড শূন্য হলে তারা জেডএফ = 1 সেট করে।
মেহেরদাদ আফশারি

43

সর্বাধিক আধুনিক স্থাপত্যগুলিতে সর্বনিম্ন সেট বিটের অবস্থান বা সর্বাধিক সেট বিট, বা শীর্ষস্থানীয় শূন্যগুলির সংখ্যা গণনা ইত্যাদির জন্য কিছু নির্দেশনা থাকবে etc.

আপনার কাছে এই শ্রেণীর কোনও একটি নির্দেশ থাকলে আপনি সস্তায় অন্যদের অনুকরণ করতে পারেন।

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

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


18

Wee, সমাধান প্রচুর পরিমাণে এবং দৃষ্টিতে একটি মানদণ্ড নয়। আপনার লোকেরা নিজেরাই লজ্জা পাবে ;-)

আমার মেশিনটি একটি ইন্টেল আই 530 (2.9 গিগাহার্টজ), উইন্ডোজ 7 64-বিট চলমান। আমি MinGW এর 32-বিট সংস্করণ দিয়ে সংকলন করেছি।

$ gcc --version
gcc.exe (GCC) 4.7.2

$ gcc bench.c -o bench.exe -std=c99 -Wall -O2
$ bench
Naive loop.         Time = 2.91  (Original questioner)
De Bruijn multiply. Time = 1.16  (Tykhyy)
Lookup table.       Time = 0.36  (Andrew Grant)
FFS instruction.    Time = 0.90  (ephemient)
Branch free mask.   Time = 3.48  (Dan / Jim Balter)
Double hack.        Time = 3.41  (DocMax)

$ gcc bench.c -o bench.exe -std=c99 -Wall -O2 -march=native
$ bench
Naive loop.         Time = 2.92
De Bruijn multiply. Time = 0.47
Lookup table.       Time = 0.35
FFS instruction.    Time = 0.68
Branch free mask.   Time = 3.49
Double hack.        Time = 0.92

আমার কোড:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


#define ARRAY_SIZE 65536
#define NUM_ITERS 5000  // Number of times to process array


int find_first_bits_naive_loop(unsigned nums[ARRAY_SIZE])
{
    int total = 0; // Prevent compiler from optimizing out the code
    for (int j = 0; j < NUM_ITERS; j++) {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            unsigned value = nums[i];
            if (value == 0)
                continue;
            unsigned pos = 0;
            while (!(value & 1))
            {
                value >>= 1;
                ++pos;
            }
            total += pos + 1;
        }
    }

    return total;
}


int find_first_bits_de_bruijn(unsigned nums[ARRAY_SIZE])
{
    static const int MultiplyDeBruijnBitPosition[32] = 
    {
       1, 2, 29, 3, 30, 15, 25, 4, 31, 23, 21, 16, 26, 18, 5, 9, 
       32, 28, 14, 24, 22, 20, 17, 8, 27, 13, 19, 7, 12, 6, 11, 10
    };

    int total = 0; // Prevent compiler from optimizing out the code
    for (int j = 0; j < NUM_ITERS; j++) {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            unsigned int c = nums[i];
            total += MultiplyDeBruijnBitPosition[((unsigned)((c & -c) * 0x077CB531U)) >> 27];
        }
    }

    return total;
}


unsigned char lowestBitTable[256];
int get_lowest_set_bit(unsigned num) {
    unsigned mask = 1;
    for (int cnt = 1; cnt <= 32; cnt++, mask <<= 1) {
        if (num & mask) {
            return cnt;
        }
    }

    return 0;
}
int find_first_bits_lookup_table(unsigned nums[ARRAY_SIZE])
{
    int total = 0; // Prevent compiler from optimizing out the code
    for (int j = 0; j < NUM_ITERS; j++) {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            unsigned int value = nums[i];
            // note that order to check indices will depend whether you are on a big 
            // or little endian machine. This is for little-endian
            unsigned char *bytes = (unsigned char *)&value;
            if (bytes[0])
                total += lowestBitTable[bytes[0]];
            else if (bytes[1])
              total += lowestBitTable[bytes[1]] + 8;
            else if (bytes[2])
              total += lowestBitTable[bytes[2]] + 16;
            else
              total += lowestBitTable[bytes[3]] + 24;
        }
    }

    return total;
}


int find_first_bits_ffs_instruction(unsigned nums[ARRAY_SIZE])
{
    int total = 0; // Prevent compiler from optimizing out the code
    for (int j = 0; j < NUM_ITERS; j++) {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            total +=  __builtin_ffs(nums[i]);
        }
    }

    return total;
}


int find_first_bits_branch_free_mask(unsigned nums[ARRAY_SIZE])
{
    int total = 0; // Prevent compiler from optimizing out the code
    for (int j = 0; j < NUM_ITERS; j++) {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            unsigned value = nums[i];
            int i16 = !(value & 0xffff) << 4;
            value >>= i16;

            int i8 = !(value & 0xff) << 3;
            value >>= i8;

            int i4 = !(value & 0xf) << 2;
            value >>= i4;

            int i2 = !(value & 0x3) << 1;
            value >>= i2;

            int i1 = !(value & 0x1);

            int i0 = (value >> i1) & 1? 0 : -32;

            total += i16 + i8 + i4 + i2 + i1 + i0 + 1;
        }
    }

    return total;
}


int find_first_bits_double_hack(unsigned nums[ARRAY_SIZE])
{
    int total = 0; // Prevent compiler from optimizing out the code
    for (int j = 0; j < NUM_ITERS; j++) {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            unsigned value = nums[i];
            double d = value ^ (value - !!value); 
            total += (((int*)&d)[1]>>20)-1022; 
        }
    }

    return total;
}


int main() {
    unsigned nums[ARRAY_SIZE];
    for (int i = 0; i < ARRAY_SIZE; i++) {
        nums[i] = rand() + (rand() << 15);
    }

    for (int i = 0; i < 256; i++) {
        lowestBitTable[i] = get_lowest_set_bit(i);
    }


    clock_t start_time, end_time;
    int result;

    start_time = clock();
    result = find_first_bits_naive_loop(nums);
    end_time = clock();
    printf("Naive loop.         Time = %.2f, result = %d\n", 
        (end_time - start_time) / (double)(CLOCKS_PER_SEC), result);

    start_time = clock();
    result = find_first_bits_de_bruijn(nums);
    end_time = clock();
    printf("De Bruijn multiply. Time = %.2f, result = %d\n", 
        (end_time - start_time) / (double)(CLOCKS_PER_SEC), result);

    start_time = clock();
    result = find_first_bits_lookup_table(nums);
    end_time = clock();
    printf("Lookup table.       Time = %.2f, result = %d\n", 
        (end_time - start_time) / (double)(CLOCKS_PER_SEC), result);

    start_time = clock();
    result = find_first_bits_ffs_instruction(nums);
    end_time = clock();
    printf("FFS instruction.    Time = %.2f, result = %d\n", 
        (end_time - start_time) / (double)(CLOCKS_PER_SEC), result);

    start_time = clock();
    result = find_first_bits_branch_free_mask(nums);
    end_time = clock();
    printf("Branch free mask.   Time = %.2f, result = %d\n", 
        (end_time - start_time) / (double)(CLOCKS_PER_SEC), result);

    start_time = clock();
    result = find_first_bits_double_hack(nums);
    end_time = clock();
    printf("Double hack.        Time = %.2f, result = %d\n", 
        (end_time - start_time) / (double)(CLOCKS_PER_SEC), result);
}

8
ডি ব্রুইজন এবং চেহারা উভয়ের জন্যই মাপদণ্ড বিভ্রান্তিমূলক হতে পারে - এর মতো শক্ত লুপে বসে প্রথম ক্রিয়াকলাপের পরে প্রতিটি লুপের অনুসন্ধানের টেবিলগুলি শেষ লুপের পরে অবধি এল 1 ক্যাশে পিন করা হবে। এটি বাস্তব-বিশ্বের ব্যবহারের সাথে মেলে না match
ম্যাটডাব্লু

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

2
আপনার এফএফএস লুপটি দুর্ভাগ্যক্রমে বিএসএফ নির্দেশের একটি মিথ্যা নির্ভরতা দ্বারা ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে কমেছে যা আপনার ক্রাস্টিস পুরাতন সংকলক এড়াতে পারে না ( তবে নতুন জিসিসি হওয়া উচিত, পপসেন্ট / এলজেন্ট / টিজিসেন্টের জন্য একইBSFতার আউটপুটটিতে একটি মিথ্যা নির্ভরতা রয়েছে (আসল আচরণের পরে যখন ইনপুট = 0 আউটপুটটি অপরিবর্তিত রাখবে) .গিসিসি দুর্ভাগ্যক্রমে লুপ পুনরাবৃত্তির মধ্যে নিবন্ধককে সাফ না করে এটিকে একটি লুপ বহনকারী নির্ভরতাতে পরিণত করে So সুতরাং বিএসএফ (3) + সিএমওভের সাথে বাধাবিহীন প্রতি 5 টি চক্রটিতে লুপটি চালানো উচিত g (2) বিলম্ব।
পিটার কর্ডেস

1
আপনার মানদণ্ডে দেখা গেছে যে এলটিউটের FFS পদ্ধতির আউটপুট প্রায় দ্বিগুণ রয়েছে, যা আমার স্থির-বিশ্লেষণের পূর্বাভাসের সাথে খুব ভাল মেলে :)। নোট করুন যে আপনি থ্রুটপুট পরিমাপ করছেন, বিলম্ব নয়, কারণ আপনার লুপের একমাত্র সিরিয়াল নির্ভরতা মোটে যোগফল। মিথ্যা নির্ভরতা না থাকলে, ffs()প্রতি ঘড়ির মধ্যে একটির (একটি 3 টি উওপ, বিএসএফের জন্য 1 এবং সিএমওভির জন্য 2 জন) থাকতে হবে এবং তারা বিভিন্ন বন্দরগুলিতে চালাতে পারে)। একই লুপ ওভারহেড সহ, এটি 7 টি ALU উফ যা প্রতি ঘড়িতে 3 এ (আপনার সিপিইউতে) চালাতে পারে। ওভারহেডের আধিপত্য! উত্স: agner.org/optimize
পিটার

1
হ্যাঁ, অফ-অর্ডার এক্সিকিউশনটি লুপটির একাধিক পুনরাবৃত্তিকে ওভারল্যাপ করতে পারে যদি এটির জন্য অপেক্ষা করতে হয় এমন কোনও ইনপুট হিসাবে bsf ecx, [ebx+edx*4]বিবেচনা না করা হয় ecx। (ইসিএক্স সর্বশেষে পূর্ববর্তী আইট্র্যাটনের সিএমওভ লিখেছিলেন)। তবে সিপিইউ সেভাবে আচরণ করে, "উত্সটি শূন্য হলে অবিচ্ছিন্ন ছেড়ে দিন" আচরণটি বাস্তবায়নের জন্য (সুতরাং এটি টিজেডিসএনটি-র মতো সত্যিকার অর্থে কোনও মিথ্যা ডিপ নয়; একটি ডেটা নির্ভরতা প্রয়োজন কারণ অনুমিতিতে কোনও শাখা নেই + অনুমানমূলক বাস্তবায়ন যে ইনপুটটি শূন্য নয়)। ইসিএক্সের উপর নির্ভরতা ভাঙতে আমরা এর xor ecx,ecxআগে একটি যোগ করে এটি কাটিয়ে উঠতে পারি bsf
পিটার কর্ডেস

17

এর দ্রুততম (নন-ইন্টারনসিক / নন-এসেমব্লার) সমাধান হ'ল সর্বনিম্ন-বাইটটি সন্ধান করুন এবং তারপরে 256-এন্ট্রি লুচিং টেবিলে সেই বাইটটি ব্যবহার করুন। এটি আপনাকে চারটি শর্তসাপেক্ষ নির্দেশাবলীর মধ্যে সবচেয়ে খারাপ পারফরম্যান্স এবং 1-এর সেরা ক্ষেত্রে দেয় 1. এটি কেবলমাত্র নির্দেশের ন্যূনতম পরিমাণই নয়, সর্বনিম্ন শাখাগুলিও যা আধুনিক হার্ডওয়্যারে অত্যন্ত গুরুত্বপূর্ণ।

আপনার টেবিলটিতে (256 8-বিট এন্ট্রি) 0-255 রেঞ্জের প্রতিটি সংখ্যার জন্য LSB এর সূচক থাকতে হবে। আপনি আপনার মানটির প্রতিটি বাইট পরীক্ষা করে নিন এবং সর্বনিম্ন অ-শূন্য বাইট খুঁজে পাবেন, তারপরে আসল সূচকটি অনুসন্ধান করতে এই মানটি ব্যবহার করুন।

এর জন্য মেমরির 256-বাইট প্রয়োজন, তবে যদি এই ফাংশনের গতিটি এত গুরুত্বপূর্ণ হয় তবে 256-বাইটগুলি এটির পক্ষে উপযুক্ত is

যেমন

byte lowestBitTable[256] = {
.... // left as an exercise for the reader to generate
};

unsigned GetLowestBitPos(unsigned value)
{
  // note that order to check indices will depend whether you are on a big 
  // or little endian machine. This is for little-endian
  byte* bytes = (byte*)value;
  if (bytes[0])
    return lowestBitTable[bytes[0]];
  else if (bytes[1])
      return lowestBitTable[bytes[1]] + 8;
  else if (bytes[2])
      return lowestBitTable[bytes[2]] + 16;
  else
      return lowestBitTable[bytes[3]] + 24;  
}

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

4
আপনি কি কোথাও একটি +8, +16, + 24 চান না?
মার্ক রান্সম

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

1
আমি এমনকি বিট শিফটও ব্যবহার করতাম (এটি প্রতি বার 8 টি করে স্থানান্তরিত)। সম্পূর্ণরূপে রেজিস্টার ব্যবহার করে করা যেতে পারে। পয়েন্টার ব্যবহার করে, আপনাকে মেমরি অ্যাক্সেস করতে হবে।
জোহানেস স্কাউব - 21

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

13

ওএমজি এটি সবেমাত্র ছড়িয়ে পড়েছে।

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

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

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

হারিয়ে যাওয়া সিপিইউ চক্রের পরিমাণ এক প্রসেসরের এক প্রকার থেকে পরেরটিতে অত্যন্ত পরিবর্তিত হয়। তবে আপনি 20 থেকে 150 এর মধ্যে হারিয়ে যাওয়া সিপিইউ চক্র আশা করতে পারেন।

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

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

স্পষ্টতই দ্রুততম ধ্রুবক সময় সমাধান হ'ল ডিটারমিনিস্টিক গণিতে জড়িত। একটি খাঁটি এবং মার্জিত সমাধান।

যদি ইতিমধ্যে এটি আবৃত থাকে তবে আমার ক্ষমা চাই।

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

মাইক্রোসফ্ট সংকলকগুলির জন্য _ বিটস্কান ফরোয়ার্ড এবং _ বিটস্ক্যানরয়েভার্স ব্যবহার করুন।
জিসিসির জন্য __ বিল্টিন_এফএস, __ বিল্টিন_ক্লিজ, __ বিল্টিন_সিটিজ ব্যবহার করুন।

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

দুঃখিত আমি কোনও সমাধান সরবরাহ করতে ভুলে গিয়েছি .. এই আইপিএডে আমি কোডটি ব্যবহার করি যা সম্পর্কে কোনও সমাবেশ স্তরের নির্দেশনা নেই:

unsigned BitScanLow_BranchFree(unsigned value)
{
    bool bwl = (value & 0x0000ffff) == 0;
    unsigned I1 = (bwl * 15);
    value = (value >> I1) & 0x0000ffff;

    bool bbl = (value & 0x00ff00ff) == 0;
    unsigned I2 = (bbl * 7);
    value = (value >> I2) & 0x00ff00ff;

    bool bnl = (value & 0x0f0f0f0f) == 0;
    unsigned I3 = (bnl * 3);
    value = (value >> I3) & 0x0f0f0f0f;

    bool bsl = (value & 0x33333333) == 0;
    unsigned I4 = (bsl * 1);
    value = (value >> I4) & 0x33333333;

    unsigned result = value + I1 + I2 + I3 + I4 - 1;

    return result;
}

এখানে বোঝার বিষয়টি এটি ব্যয়বহুল তুলনা নয়, তবে তুলনা করার পরে যে শাখাটি ঘটে তা। এই ক্ষেত্রে তুলনাটি .. == 0 এর সাথে 0 বা 1 এর মানতে বাধ্য করা হয়, এবং ফলাফলটি শাখার উভয় পাশে যে গণিতটি ঘটেছিল তা একত্রিত করতে ব্যবহৃত হয়।

সম্পাদনা:

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

int BitScanLow_BranchFree(ui value)
{
    int i16 = !(value & 0xffff) << 4;
    value >>= i16;

    int i8 = !(value & 0xff) << 3;
    value >>= i8;

    int i4 = !(value & 0xf) << 2;
    value >>= i4;

    int i2 = !(value & 0x3) << 1;
    value >>= i2;

    int i1 = !(value & 0x1);

    int i0 = (value >> i1) & 1? 0 : -32;

    return i16 + i8 + i4 + i2 + i1 + i0;
}

এটি দেওয়া হলে -১ প্রদান করে। ০. যদি আপনি 0 সম্পর্কে চিন্তা করেন না বা 0 এর জন্য 31 পেয়ে খুশি হন তবে i0 গণনাটি সরিয়ে ফেলুন, অনেকটা সময় সাশ্রয় করুন।


3
আমি আপনার জন্য এটি স্থির করেছি। আপনি যা পোস্ট করেন তা পরীক্ষা করে দেখুন।
জিম বাল্টার

5
যখন সেখানে কোনও টার্নারি অপারেটর অন্তর্ভুক্ত থাকবে তখন আপনি কীভাবে এটিকে "শাখা মুক্ত" বলতে পারেন?
বোল্টবাইট

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

FWIW জিসিসি এমনকি শাখাগুলোর উত্পন্ন -O3 godbolt.org/z/gcsUHd
Qix - মনিকা দুর্ব্যবহার ছিলেন

7

একটি সেট বিট অনুসন্ধান করা জড়িত এই অনুরূপ পোস্ট দ্বারা অনুপ্রাণিত , আমি নিম্নলিখিত অফার:

unsigned GetLowestBitPos(unsigned value)
{
   double d = value ^ (value - !!value); 
   return (((int*)&d)[1]>>20)-1023; 
}

পেশাদাররা:

  • কোন লুপ নেই
  • কোন শাখা নেই
  • ধ্রুব সময়ে চালায়
  • হ্যান্ডেল মান = 0 একটি অন্যথায়-সীমানা ফলাফল ফিরে দ্বারা
  • কোড দুটি মাত্র লাইন

কনস:

  • কোডড হিসাবে সামান্য endianness ধরে (স্থির পরিবর্তন করে স্থির করা যেতে পারে)
  • ধরে নেওয়া যায় যে ডাবলটি আসল * 8 আইইইই ফ্লোট (আইইইই 754)

আপডেট: মন্তব্যে উল্লিখিত হিসাবে, একটি ইউনিয়ন একটি পরিষ্কার বাস্তবায়ন (সি এর জন্য, কমপক্ষে) এবং এর মতো দেখতে হবে:

unsigned GetLowestBitPos(unsigned value)
{
    union {
        int i[2];
        double d;
    } temp = { .d = value ^ (value - !!value) };
    return (temp.i[1] >> 20) - 1023;
}

এটি প্রতিটি কিছুর জন্য স্বল্প-এন্ডিয়ান স্টোরেজ সহ 32-বিট ইনটকে ধরে নেয় (x86 প্রসেসারগুলি ভাবেন)।


1
আকর্ষণীয় - আমি বিট পাটিগণ্যের জন্য ডাবলগুলি ব্যবহার করতে এখনও ভয় পাই, তবে আমি এটি মনে রাখব
পিটারচেন

ফ্রিক্সপ () ব্যবহার করা এটিকে কিছুটা পোর্টেবল করে তুলতে পারে
aka.nice

1
পয়েন্টার-কাস্টিং দ্বারা টাইপ-পাণিং সি বা সি ++ এ নিরাপদ নয়। সি ++ তে মেমকি বা সি-তে একটি ইউনিয়ন ব্যবহার করুন (বা আপনার সংকলক এটি নিরাপদ করার নিশ্চয়তা দিলে উদাহরণস্বরূপ, সি ++ এর GNU এক্সটেনশানগুলি (অনেক সংকলক দ্বারা সমর্থিত) ইউনিয়ন টাইপ-পাঞ্জিং গ্যারান্টি দেয়))
পিটার কর্ডস

1
পুরানো জিসিসি পয়েন্টার-কাস্টের পরিবর্তে ইউনিয়নের সাথে আরও ভাল কোড তৈরি করে: এটি স্টোর / পুনরায় লোড করার পরিবর্তে সরাসরি একটি এফপি রেগ (xmm0) থেকে র্যাক্স (মুভাক সহ) এ সরানো হয়। আরও নতুন জিসিসি এবং কলং উভয় উপায়ে মুভাক ব্যবহার করে। ইউনিয়ন সংস্করণের জন্য Godbolt.org/g/x7JBiL দেখুন । আপনি কি 20 দ্বারা একটি গাণিতিক শিফট করছেন তা উদ্দেশ্যমূলক? তোমার অনুমানের তালিকায় যে উচিত intনয় int32_t, এবং যে স্বাক্ষর অধিকার শিফট একটি গাণিতিক শিফট (C ++ এটা বাস্তবায়ন-সংজ্ঞায়িত) হল
পিটার Cordes

1
এছাড়াও বিটিডাব্লু, ভিজ্যুয়াল স্টুডিও (কমপক্ষে 2013) পরীক্ষা / সেটসিটিসি / উপ পদ্ধতি ব্যবহার করে। আমি নিজের চেয়ে ভাল সিএমপি / এডিসি পছন্দ করি।
ডকম্যাক্স

5

এটি 32 টিরও কম অপারেশনের সবচেয়ে খারাপ ক্ষেত্রে করা যেতে পারে:

নীতি: 2 বা ততোধিক বিট পরীক্ষা করা ঠিক 1 বিটের জন্য পরীক্ষা করার মতো দক্ষ।

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

সুতরাং ...
আপনি যদি একবারে 2 টি বিট পরীক্ষা করেন তবে আপনার নিকৃষ্ট অবস্থাতে (নবিটস / 2) + 1 টি মোট চেক রয়েছে।
যদি আপনি একবারে 3 টি বিট পরীক্ষা করেন তবে আপনার নিকৃষ্টতম অবস্থাতে (নবিটস / 3) + 2 টি মোট চেক রয়েছে।
...

সর্বোত্তম 4 টি গ্রুপে চেক করা হবে যা আপনার 32 এর পরিবর্তে সবচেয়ে খারাপ ক্ষেত্রে 11 ক্রিয়াকলাপের প্রয়োজন হবে।

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

দ্রষ্টব্য: আমি লুপটি ব্যবহার না করে এটি পুরোপুরি লিখি কারণ এটি সেভাবে আরও দক্ষ।

int getLowestBitPos(unsigned int value)
{
    //Group 1: Bits 0-3
    if(value&0xf)
    {
        if(value&0x1)
            return 0;
        else if(value&0x2)
            return 1;
        else if(value&0x4)
            return 2;
        else
            return 3;
    }

    //Group 2: Bits 4-7
    if(value&0xf0)
    {
        if(value&0x10)
            return 4;
        else if(value&0x20)
            return 5;
        else if(value&0x40)
            return 6;
        else
            return 7;
    }

    //Group 3: Bits 8-11
    if(value&0xf00)
    {
        if(value&0x100)
            return 8;
        else if(value&0x200)
            return 9;
        else if(value&0x400)
            return 10;
        else
            return 11;
    }

    //Group 4: Bits 12-15
    if(value&0xf000)
    {
        if(value&0x1000)
            return 12;
        else if(value&0x2000)
            return 13;
        else if(value&0x4000)
            return 14;
        else
            return 15;
    }

    //Group 5: Bits 16-19
    if(value&0xf0000)
    {
        if(value&0x10000)
            return 16;
        else if(value&0x20000)
            return 17;
        else if(value&0x40000)
            return 18;
        else
            return 19;
    }

    //Group 6: Bits 20-23
    if(value&0xf00000)
    {
        if(value&0x100000)
            return 20;
        else if(value&0x200000)
            return 21;
        else if(value&0x400000)
            return 22;
        else
            return 23;
    }

    //Group 7: Bits 24-27
    if(value&0xf000000)
    {
        if(value&0x1000000)
            return 24;
        else if(value&0x2000000)
            return 25;
        else if(value&0x4000000)
            return 26;
        else
            return 27;
    }

    //Group 8: Bits 28-31
    if(value&0xf0000000)
    {
        if(value&0x10000000)
            return 28;
        else if(value&0x20000000)
            return 29;
        else if(value&0x40000000)
            return 30;
        else
            return 31;
    }

    return -1;
}

আমার কাছ থেকে +1 এটি দ্রুততম নয় তবে এটি মূলের চেয়ে দ্রুততর, এটিই ছিল ...
অ্যান্ড্রু গ্রান্ট

@ ওয়ানবিওন.লাইভজার্নাল.কম: কোডটিতে কোনও ত্রুটি থাকলেও, গ্রুপিংয়ের ধারণাটি আমি যে বিষয়টি পেরে যাওয়ার চেষ্টা করছিলাম is প্রকৃত কোডের নমুনাটি খুব বেশি গুরুত্ব দেয় না এবং এটি আরও কমপ্যাক্ট তবে কম দক্ষ করা যায়।
ব্রায়ান আর বন্ডি

আমি কেবল ভাবছি যে আমার উত্তরের সত্যিকারের খারাপ অংশ আছে কিনা, বা লোকেরা যদি এটি পছন্দ না করে তবে আমি এটি পুরোপুরি লিখেছিলাম?
ব্রায়ান আর বন্ডি 18

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

@ ওয়ানবিওন.লাইভজার্নাল.কম: ... এটি কম অপারেশনস তা জানতে আমার উপরের কোডটির প্রয়োজন নেই। আমি স্পষ্ট দেখতে পাচ্ছি। আমি কোনও দাবি করি নি যার জন্য প্রোফাইলিং প্রয়োজন।
ব্রায়ান আর বন্ডি

4

বাইনারি অনুসন্ধান কেন ব্যবহার করবেন না ? এটি সর্বদা 5 টি অপারেশনের পরে সম্পন্ন হবে (4 বাইটের অভ্যন্তরীণ আকার ধরে):

if (0x0000FFFF & value) {
    if (0x000000FF & value) {
        if (0x0000000F & value) {
            if (0x00000003 & value) {
                if (0x00000001 & value) {
                    return 1;
                } else {
                    return 2;
                }
            } else {
                if (0x0000004 & value) {
                    return 3;
                } else {
                    return 4;
                }
            }
        } else { ...
    } else { ...
} else { ...

+1 এটি আমার উত্তরের সাথে খুব মিল। সবচেয়ে ভাল কেস রান সময় আমার পরামর্শের চেয়ে খারাপ, তবে সবচেয়ে খারাপ ক্ষেত্রে রান সময় ভাল।
ব্রায়ান আর বন্ডি

2

অন্য একটি পদ্ধতি (মডুলাস বিভাগ এবং অনুসন্ধান) @ অ্যান্টন-টাইখায়ি দ্বারা সরবরাহিত একই লিঙ্কটি থেকে এখানে একটি বিশেষ উল্লেখের দাবি রাখে। এই পদ্ধতিটি সামান্য তবে গুরুত্বপূর্ণ পার্থক্যের সাথে DeBruijn গুণমান এবং অনুসন্ধান পদ্ধতিতে পারফরম্যান্সের সাথে খুব মিল।

মডুলাস বিভাগ এবং অনুসন্ধান

 unsigned int v;  // find the number of trailing zeros in v
    int r;           // put the result in r
    static const int Mod37BitPosition[] = // map a bit value mod 37 to its position
    {
      32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
      7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
      20, 8, 19, 18
    };
    r = Mod37BitPosition[(-v & v) % 37];

মডুলাস বিভাগ এবং অনুসন্ধান পদ্ধতিটি v = 0x00000000 এবং v = FFFFFFFF এর জন্য পৃথক মান প্রদান করে যখন ডিব্রাইজন গুন এবং অনুসন্ধান পদ্ধতি উভয় ইনপুটগুলিতে শূন্য প্রদান করে।

পরীক্ষা: -

unsigned int n1=0x00000000, n2=0xFFFFFFFF;

MultiplyDeBruijnBitPosition[((unsigned int )((n1 & -n1) * 0x077CB531U)) >> 27]); /* returns 0 */
MultiplyDeBruijnBitPosition[((unsigned int )((n2 & -n2) * 0x077CB531U)) >> 27]); /* returns 0 */
Mod37BitPosition[(((-(n1) & (n1))) % 37)]); /* returns 32 */
Mod37BitPosition[(((-(n2) & (n2))) % 37)]); /* returns 0 */

1
modধীর. পরিবর্তে, আপনি মূল গুণ-এবং-লুকআপ পদ্ধতি ব্যবহার এবং বিয়োগ করতে পারেন !vথেকে rপ্রান্ত মামলা পরিচালনা করতে।
ইটান টি

3
@ আইটানটি একটি অপ্টিমাইজার খুব ভালভাবে সেই মোডকে দ্রুত গুনে রূপান্তর করতে পারে হ্যাকারদের আনন্দের মতো
ফুচলভি

2

মতে দাবা BitScan পৃষ্ঠা প্রোগ্রামিং এবং আমার নিজস্ব পরিমাপ, বিয়োগ এবং XOR অস্বীকার তুলনায় দ্রুততর এবং মাস্ক হয়।

(আপনি যদি অনুমানযোগ্য শূন্যগুলি গণনা করতে যাচ্ছেন তার চেয়ে নোট করুন 0, যে পদ্ধতিটি আমার কাছে রয়েছে তেমন ফিরে আসে 63এবং উপেক্ষা এবং মুখোশ ফিরে আসে0 ))

এখানে একটি -৪-বিট বিয়োগ এবং জোর:

unsigned long v;  // find the number of trailing zeros in 64-bit v 
int r;            // result goes here
static const int MultiplyDeBruijnBitPosition[64] = 
{
  0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61,
  54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62,
  46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
  25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63
};
r = MultiplyDeBruijnBitPosition[((uint32_t)((v ^ (v-1)) * 0x03F79D71B4CB0A89U)) >> 58];

রেফারেন্সের জন্য, এখানে প্রত্যাখ্যান এবং মুখোশ পদ্ধতির একটি 64-বিট সংস্করণ রয়েছে:

unsigned long v;  // find the number of trailing zeros in 64-bit v 
int r;            // result goes here
static const int MultiplyDeBruijnBitPosition[64] = 
{
  0, 1, 48, 2, 57, 49, 28, 3, 61, 58, 50, 42, 38, 29, 17, 4,
  62, 55, 59, 36, 53, 51, 43, 22, 45, 39, 33, 30, 24, 18, 12, 5,
  63, 47, 56, 27, 60, 41, 37, 16, 54, 35, 52, 21, 44, 32, 23, 11,
  46, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6
};
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x03F79D71B4CB0A89U)) >> 58];

এই (v ^ (v-1))কাজ দেওয়া v != 0। যদি এর ক্ষেত্রে v == 00xFF ফেরত আসে .... FF (v & -v)শূন্য দেয় (যা উপায়টিও ভুল, তবে বুফ অন্তত এটি একটি যুক্তিসঙ্গত ফলাফলের দিকে নিয়ে যায়)।
সিয়াপান

@ সিয়াপান: এটি একটি ভাল বিষয়, আমি এটি উল্লেখ করব। আমি অনুমান করছি যে এখানে আলাদা ডি ব্রুইজন সংখ্যা রয়েছে যা resolve৩ তম সূচকটিতে 0 রেখে এটি সমাধান করবে।
jnm2

দুহ, সমস্যাটি সেখানেই নেই। 0 এবং 0x8000000000000000 উভয়ের পরে 0xFFFFFFFFFFFFFFFFFF ফলাফল হয় v ^ (v-1), তাই তাদের আলাদা করে বলার অপেক্ষা রাখে না। আমার দৃশ্যে শূন্য কখনই ইনপুট হবে না।
jnm2

1

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

32 বিট ইন - প্রথম 16 টির মধ্যে কোনও সেট করা আছে কিনা তা পরীক্ষা করে দেখুন। যদি তা হয় তবে প্রথম 8 টির কোনও সেট আছে কিনা তা পরীক্ষা করে দেখুন। যদি তাই, ....

যদি তা না হয় তবে উপরের 16 টির মধ্যে কোনও সেট আছে কিনা তা পরীক্ষা করুন ..

মূলত এটি বাইনারি অনুসন্ধান।


1

কোনও একক x86 নির্দেশ দিয়ে এটি কীভাবে করবেন তার জন্য আমার উত্তরটি এখানে দেখুন , কেবলমাত্র বর্ণিত পরিবর্তে আপনি ("বিট স্ক্যান ফরোয়ার্ড") নির্দেশনাটি চাইবেন এমন ন্যূনতম উল্লেখযোগ্য সেট বিটটি খুঁজে পেতে ।BSFBSR


1

তবুও আরেকটি সমাধান, সম্ভবত দ্রুত নয়, তবে বেশ ভাল বলে মনে হচ্ছে।
কমপক্ষে এর কোনও শাখা নেই। ;)

uint32 x = ...;  // 0x00000001  0x0405a0c0  0x00602000
x |= x <<  1;    // 0x00000003  0x0c0fe1c0  0x00e06000
x |= x <<  2;    // 0x0000000f  0x3c3fe7c0  0x03e1e000
x |= x <<  4;    // 0x000000ff  0xffffffc0  0x3fffe000
x |= x <<  8;    // 0x0000ffff  0xffffffc0  0xffffe000
x |= x << 16;    // 0xffffffff  0xffffffc0  0xffffe000

// now x is filled with '1' from the least significant '1' to bit 31

x = ~x;          // 0x00000000  0x0000003f  0x00001fff

// now we have 1's below the original least significant 1
// let's count them

x = x & 0x55555555 + (x >>  1) & 0x55555555;
                 // 0x00000000  0x0000002a  0x00001aaa

x = x & 0x33333333 + (x >>  2) & 0x33333333;
                 // 0x00000000  0x00000024  0x00001444

x = x & 0x0f0f0f0f + (x >>  4) & 0x0f0f0f0f;
                 // 0x00000000  0x00000006  0x00000508

x = x & 0x00ff00ff + (x >>  8) & 0x00ff00ff;
                 // 0x00000000  0x00000006  0x0000000d

x = x & 0x0000ffff + (x >> 16) & 0x0000ffff;
                 // 0x00000000  0x00000006  0x0000000d
// least sign.bit pos. was:  0           6          13

1সর্বনিম্ন উল্লেখযোগ্য 1 থেকে এলএসবি-তে সমস্ত পেতে , ((x & -x) - 1) << 1পরিবর্তে ব্যবহার করুন
ফুচলভি

আরও দ্রুততর উপায়:x ^ (x-1)
ফুচলভি

1
unsigned GetLowestBitPos(unsigned value)
{
    if (value & 1) return 1;
    if (value & 2) return 2;
    if (value & 4) return 3;
    if (value & 8) return 4;
    if (value & 16) return 5;
    if (value & 32) return 6;
    if (value & 64) return 7;
    if (value & 128) return 8;
    if (value & 256) return 9;
    if (value & 512) return 10;
    if (value & 1024) return 11;
    if (value & 2048) return 12;
    if (value & 4096) return 13;
    if (value & 8192) return 14;
    if (value & 16384) return 15;
    if (value & 32768) return 16;
    if (value & 65536) return 17;
    if (value & 131072) return 18;
    if (value & 262144) return 19;
    if (value & 524288) return 20;
    if (value & 1048576) return 21;
    if (value & 2097152) return 22;
    if (value & 4194304) return 23;
    if (value & 8388608) return 24;
    if (value & 16777216) return 25;
    if (value & 33554432) return 26;
    if (value & 67108864) return 27;
    if (value & 134217728) return 28;
    if (value & 268435456) return 29;
    if (value & 536870912) return 30;
    return 31;
}

সমস্ত সংখ্যার 50% কোডের প্রথম লাইনে ফিরে আসবে।

সমস্ত সংখ্যার 75% কোডের প্রথম 2 লাইনে ফিরে আসবে।

সমস্ত সংখ্যার 87% কোডের প্রথম 3 লাইনে ফিরে আসবে।

সমস্ত সংখ্যার 94% কোডের প্রথম 4 লাইনে ফিরে আসবে।

সমস্ত সংখ্যার 97% কোডের প্রথম 5 লাইনে ফিরে আসবে।

প্রভৃতি

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


3
এবং 32 শাখার

1
এটি কি অন্তত একটি সুইচ হিসাবে তৈরি করা যায়নি ...?
স্টিভেন লু

"এটি কি কমপক্ষে একটি সুইচ হিসাবে তৈরি করা যায়নি ...?" এটি সম্ভব হওয়ার ইঙ্গিত দেওয়ার আগে আপনি কি তা করার চেষ্টা করেছিলেন? আপনি যখন থেকে একটি স্যুইচ এর ক্ষেত্রে গণনা করতে পারেন? এটি একটি দেখার টেবিল, কোনও শ্রেণি নয়।
j রিভ

1

"প্রোগ্রামিংয়ের শিল্পকলা, অংশ 4" এ 'ম্যাজিক মাস্ক' ব্যবহার করে এই চতুর কৌশলটি পেয়েছে, যা এটি এন-বিট সংখ্যার জন্য ও (লগ (এন)) সময়ে করে। [লগ (এন) অতিরিক্ত স্থান সহ]। সেট বিটের জন্য সাধারন সমাধানগুলি হ'ল হয় ও (এন) হয় বা দেখার জন্য টেবিলের জন্য ও (এন) অতিরিক্ত স্থানের প্রয়োজন হয়, সুতরাং এটি একটি ভাল আপস।

যাদু মুখোশগুলি:

m0 = (...............01010101)  
m1 = (...............00110011)
m2 = (...............00001111)  
m3 = (.......0000000011111111)
....

মূল ধারণা: x = 1 * [(x & m0) = 0] + 2 * [(এক্স এবং এম 1) = 0] + 4 * [(এক্স এবং এম 2) = 0] + ... তে শূন্যের পিছনে কোনও নয়

int lastSetBitPos(const uint64_t x) {
    if (x == 0)  return -1;

    //For 64 bit number, log2(64)-1, ie; 5 masks needed
    int steps = log2(sizeof(x) * 8); assert(steps == 6);
    //magic masks
    uint64_t m[] = { 0x5555555555555555, //     .... 010101
                     0x3333333333333333, //     .....110011
                     0x0f0f0f0f0f0f0f0f, //     ...00001111
                     0x00ff00ff00ff00ff, //0000000011111111 
                     0x0000ffff0000ffff, 
                     0x00000000ffffffff };

    //Firstly extract only the last set bit
    uint64_t y = x & -x;

    int trailZeros = 0, i = 0 , factor = 0;
    while (i < steps) {
        factor = ((y & m[i]) == 0 ) ? 1 : 0;
        trailZeros += factor * pow(2,i);
        ++i;
    }
    return (trailZeros+1);
}

1

যদি সি ++ 11 আপনার জন্য উপলভ্য থাকে তবে একটি সংকলক কখনও কখনও আপনার জন্য কাজটি করতে পারে :)

constexpr std::uint64_t lssb(const std::uint64_t value)
{
    return !value ? 0 : (value % 2 ? 1 : lssb(value >> 1) + 1);
}

ফলাফলটি 1-ভিত্তিক সূচক।


1
চালাক, তবে ইনপুটটি সংকলন-সময় ধ্রুবক না হলে এটি বিপর্যয়করভাবে খারাপ সমাবেশে সংকলন করে। Godbolt.org/g/7ajMyT । (জিসিসি, বা ঝনঝন সঙ্গে একটি প্রকৃত রিকার্সিভ ফাংশন কলের মাধ্যমে বিট উপর একটি মূক লুপ।) জিসিসি / ঝনঝন মূল্যায়ন করতে পারেন ffs()যাতে আপনি কাজের জন্য নির্দিষ্ট-প্রসারণ এই ব্যবহার করতে হবে না, কম্পাইল সময়ে। (অবশ্যই আপনাকে অবশ্যই ইনলাইন-এ্যাসেম এড়াতে হবে)) আপনার যদি সত্যই এমন কিছু প্রয়োজন হয় যা সি ++ 11 হিসাবে কাজ করে তবে constexprআপনি এখনও জিএনইউ সি ব্যবহার করতে পারেন __builtin_ffs
পিটার

0

এটি @ অ্যান্টন টাইখ্যি উত্তর সম্পর্কিত ards

এখানে আমার সি ++ 11 কনটেক্সপ্রস বাস্তবায়ন কাস্টগুলি বাদ দিয়ে এবং 32 বিটের একটি 64 বিটের ফলাফল কেটে ভিসি ++ 17 এ একটি সতর্কতা অপসারণ করছে:

constexpr uint32_t DeBruijnSequence[32] =
{
    0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
    31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
constexpr uint32_t ffs ( uint32_t value )
{
    return  DeBruijnSequence[ 
        (( ( value & ( -static_cast<int32_t>(value) ) ) * 0x077CB531ULL ) & 0xFFFFFFFF)
            >> 27];
}

0x1 এবং 0x0 উভয়ই 0 ফেরতের ইস্যুটি পেতে আপনি করতে পারেন:

constexpr uint32_t ffs ( uint32_t value )
{
    return (!value) ? 32 : DeBruijnSequence[ 
        (( ( value & ( -static_cast<int32_t>(value) ) ) * 0x077CB531ULL ) & 0xFFFFFFFF)
            >> 27];
}

তবে যদি সংকলক কলটি প্রস্রোস করতে না পারে বা না করে তবে এটি গণনায় কয়েক চক্র যুক্ত করবে।

অবশেষে, যদি আগ্রহী হয়, কোডটি যা করতে চেষ্টা করছে তা যাচাই করে তা পরীক্ষা করার জন্য স্থিতিক দৃ as়তার একটি তালিকা এখানে রয়েছে:

static_assert (ffs(0x1) == 0, "Find First Bit Set Failure.");
static_assert (ffs(0x2) == 1, "Find First Bit Set Failure.");
static_assert (ffs(0x4) == 2, "Find First Bit Set Failure.");
static_assert (ffs(0x8) == 3, "Find First Bit Set Failure.");
static_assert (ffs(0x10) == 4, "Find First Bit Set Failure.");
static_assert (ffs(0x20) == 5, "Find First Bit Set Failure.");
static_assert (ffs(0x40) == 6, "Find First Bit Set Failure.");
static_assert (ffs(0x80) == 7, "Find First Bit Set Failure.");
static_assert (ffs(0x100) == 8, "Find First Bit Set Failure.");
static_assert (ffs(0x200) == 9, "Find First Bit Set Failure.");
static_assert (ffs(0x400) == 10, "Find First Bit Set Failure.");
static_assert (ffs(0x800) == 11, "Find First Bit Set Failure.");
static_assert (ffs(0x1000) == 12, "Find First Bit Set Failure.");
static_assert (ffs(0x2000) == 13, "Find First Bit Set Failure.");
static_assert (ffs(0x4000) == 14, "Find First Bit Set Failure.");
static_assert (ffs(0x8000) == 15, "Find First Bit Set Failure.");
static_assert (ffs(0x10000) == 16, "Find First Bit Set Failure.");
static_assert (ffs(0x20000) == 17, "Find First Bit Set Failure.");
static_assert (ffs(0x40000) == 18, "Find First Bit Set Failure.");
static_assert (ffs(0x80000) == 19, "Find First Bit Set Failure.");
static_assert (ffs(0x100000) == 20, "Find First Bit Set Failure.");
static_assert (ffs(0x200000) == 21, "Find First Bit Set Failure.");
static_assert (ffs(0x400000) == 22, "Find First Bit Set Failure.");
static_assert (ffs(0x800000) == 23, "Find First Bit Set Failure.");
static_assert (ffs(0x1000000) == 24, "Find First Bit Set Failure.");
static_assert (ffs(0x2000000) == 25, "Find First Bit Set Failure.");
static_assert (ffs(0x4000000) == 26, "Find First Bit Set Failure.");
static_assert (ffs(0x8000000) == 27, "Find First Bit Set Failure.");
static_assert (ffs(0x10000000) == 28, "Find First Bit Set Failure.");
static_assert (ffs(0x20000000) == 29, "Find First Bit Set Failure.");
static_assert (ffs(0x40000000) == 30, "Find First Bit Set Failure.");
static_assert (ffs(0x80000000) == 31, "Find First Bit Set Failure.");

0

লগ সন্ধান করা কিছুটা ব্যয়বহুল হলেও, এখানে একটি সহজ বিকল্প।

if(n == 0)
  return 0;
return log2(n & -n)+1;   //Assuming the bit index starts from 1

-3

সম্প্রতি আমি দেখতে পাচ্ছি যে সিঙ্গাপুরের প্রধানমন্ত্রী ফেসবুকে তিনি লিখেছেন এমন একটি প্রোগ্রাম পোস্ট করেছেন, এটির উল্লেখ করার জন্য একটি লাইন আছে ..

লজিকটি কেবল "মান এবং মূল্য" হয়, ধরুন আপনার 0x0FF0 আছে, তাহলে 0FF0 এবং (F00F + 1), যা 0x0010 এর সমান, যার অর্থ সর্বনিম্ন 1 টি 4 র্থ বিটের মধ্যে রয়েছে .. :)


1
এটি সর্বনিম্ন বিটকে পৃথক করে তবে আপনাকে তার অবস্থান দেয় না যা এই প্রশ্নটি যা জিজ্ঞাসা করছে।
রাশিমোটো

আমি মনে করি না এটি শেষ বিটটি অনুসন্ধানের জন্য কাজ করে।
yyny

মান & ~ মান 0 হয়
khw

ওফ, আমার চোখ খারাপ হচ্ছে bad আমি টিলডের জন্য বিয়োগ ভুল করেছিলাম। আমার মন্তব্য উপেক্ষা করুন
khw

-8

আপনার যদি সংস্থান থাকে তবে গতি উন্নত করতে আপনি স্মৃতি ত্যাগ করতে পারেন:

static const unsigned bitPositions[MAX_INT] = { 0, 0, 1, 0, 2, /* ... */ };

unsigned GetLowestBitPos(unsigned value)
{
    assert(value != 0); // handled separately
    return bitPositions[value];
}

দ্রষ্টব্য: এই টেবিলটি কমপক্ষে 4 জিবি গ্রহণ করবে (যদি আমরা ফেরতের ধরণটি ছেড়ে যাই তবে 16 গিগাবাইট unsigned)। এটি অন্যটির জন্য একটি সীমিত সংস্থান (র‌্যাম) ব্যবসায়ের উদাহরণ (সম্পাদনের গতি)।

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


1
ইনপুটটির পরিসরটি ইতিমধ্যে প্যারামিটার ধরণের দ্বারা সুনির্দিষ্ট করা হয়েছে - 'স্বাক্ষরবিহীন' একটি 32-বিট মান তাই না, আপনি ভাল না।
ব্রায়ান

3
উম্ম ... আপনার পৌরাণিক সিস্টেম এবং ওএসের পেজড মেমরির ধারণা নেই? কত সময় ব্যয় হতে যাচ্ছে?
মাইকেজ

14
এটি একটি উত্তর নেই। আপনার সমাধান সমস্ত রিয়েল-ওয়ার্ল্ড অ্যাপ্লিকেশনগুলিতে সম্পূর্ণ অবাস্তব এবং এটিকে "ট্রেড অফ" বলা অসম্পূর্ণ। আপনার পৌরাণিক সিস্টেমটিতে যে কোনও একক ক্রিয়াকলাপে উত্সর্গ করতে 16 গিগাবাইট র‌্যাম রয়েছে কেবল তার অস্তিত্ব নেই। আপনি "কোয়ান্টাম কম্পিউটার ব্যবহার করুন" এর উত্তরও দিয়ে যাচ্ছিলেন।
ব্রায়ান

3
গতির জন্য বলি স্মৃতি? একটি 4 গিগাবাইট + অনুসন্ধানের টেবিলটি বর্তমানে বিদ্যমান কোনও মেশিনে কখনও ক্যাশে মাপসই করা হবে না, তাই আমি কল্পনা করতে পারি এটি সম্ভবত এখানে অন্যান্য সমস্ত উত্তরগুলির চেয়ে ধীর।

1
আহা। এই ভয়ঙ্কর উত্তরটি আমাকে :)@ দান করতে থাকে: আপনি মেমরি ক্যাশিং সম্পর্কে সঠিক। উপরে মাইকেজের মন্তব্য দেখুন।
জেমস 23
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.