% অপারেটরের চেয়ে আরও দ্রুত বিভাজ্যতা পরীক্ষা?


23

আমি আমার কম্পিউটারে একটি কৌতূহলী জিনিস লক্ষ্য করেছি। * হাতে লিখিত বিভাজ্যতা পরীক্ষা %অপারেটরের তুলনায় উল্লেখযোগ্যভাবে দ্রুত is সর্বনিম্ন উদাহরণ বিবেচনা করুন:

* এএমডি রাইজেন থ্রেড্রিপার 2990WX, জিসিসি 9.2.0

static int divisible_ui_p(unsigned int m, unsigned int a)
{
    if (m <= a) {
        if (m == a) {
            return 1;
        }

        return 0;
    }

    m += a;

    m >>= __builtin_ctz(m);

    return divisible_ui_p(m, a);
}

উদাহরণটি বিজোড় aএবং দ্বারা সীমাবদ্ধ m > 0। তবে এটি সহজেই সকলের কাছে সাধারণীকরণ করা যায় aএবং m। কোডটি কেবল বিভাগটিকে সংযোজনগুলির একটি সিরিজে রূপান্তর করে।

এখন পরীক্ষা প্রোগ্রামটি সংকলিত বিবেচনা করুন -std=c99 -march=native -O3:

    for (unsigned int a = 1; a < 100000; a += 2) {
        for (unsigned int m = 1; m < 100000; m += 1) {
#if 1
            volatile int r = divisible_ui_p(m, a);
#else
            volatile int r = (m % a == 0);
#endif
        }
    }

... এবং আমার কম্পিউটারে ফলাফল:

| implementation     | time [secs] |
|--------------------|-------------|
| divisible_ui_p     |    8.52user |
| builtin % operator |   17.61user |

অতএব 2 গুণ বেশি দ্রুত।

প্রশ্ন: কোডটি আপনার মেশিনে কীভাবে আচরণ করে তা আমাকে বলতে পারেন? এটি কী জিসিসিতে অপ্টিমাইজেশনের সুযোগ মিস করেছে? আপনি আরও দ্রুত এই পরীক্ষা করতে পারেন?


আপডেট: অনুরোধ হিসাবে, এখানে একটি সংক্ষিপ্ত পুনরুত্পাদনযোগ্য উদাহরণ:

#include <assert.h>

static int divisible_ui_p(unsigned int m, unsigned int a)
{
    if (m <= a) {
        if (m == a) {
            return 1;
        }

        return 0;
    }

    m += a;

    m >>= __builtin_ctz(m);

    return divisible_ui_p(m, a);
}

int main()
{
    for (unsigned int a = 1; a < 100000; a += 2) {
        for (unsigned int m = 1; m < 100000; m += 1) {
            assert(divisible_ui_p(m, a) == (m % a == 0));
#if 1
            volatile int r = divisible_ui_p(m, a);
#else
            volatile int r = (m % a == 0);
#endif
        }
    }

    return 0;
}

gcc -std=c99 -march=native -O3 -DNDEBUGএএমডি রাইজেন থ্রেড্রিপার 2990WX এর সাথে সংকলিত

gcc --version
gcc (Gentoo 9.2.0-r2 p3) 9.2.0

আপডেট 2: অনুরোধ অনুসারে, যে সংস্করণটি যে কোনওটি পরিচালনা করতে পারে aএবং m(আপনি যদি পূর্ণসংখ্যার ওভারফ্লো এড়াতে চান তবে ইনপুট পূর্ণসংখ্যার চেয়ে দ্বিগুণ দীর্ঘ সময় পূর্ণসংখ্যা টাইপের সাথে পরীক্ষাটি প্রয়োগ করতে হবে):

int divisible_ui_p(unsigned int m, unsigned int a)
{
#if 1
    /* handles even a */
    int alpha = __builtin_ctz(a);

    if (alpha) {
        if (__builtin_ctz(m) < alpha) {
            return 0;
        }

        a >>= alpha;
    }
#endif

    while (m > a) {
        m += a;
        m >>= __builtin_ctz(m);
    }

    if (m == a) {
        return 1;
    }

#if 1
    /* ensures that 0 is divisible by anything */
    if (m == 0) {
        return 1;
    }
#endif

    return 0;
}

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

আমি এমন একটি পরীক্ষাও দেখতে চাই যেখানে আপনি আসলে দৃ .়ভাবে দাবি করেছেন যে আপনি যে দুটি rগণনা করেছেন তা সত্যই একে অপরের সমান।
মাইক নকিস

@ মাইকনাকিস আমি কেবল এটি যোগ করেছি।
ডাবলার

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

উত্তর:


11

আপনি যা করছেন তা শক্তি হ্রাস বলা হয়: একটি ব্যয়বহুল অপারেশনের পরিবর্তে কয়েকটি সস্তার জিনিস with

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

শেষ অবধি, সচেতন থাকুন যে নির্দিষ্ট ধ্রুবক দ্বারা বিভাগের বাকী অংশ নেওয়ার জন্য অনেকগুলি শর্টকাট রয়েছে। (যদিও সংকলকগণ সাধারণত আপনার জন্য এটি যত্ন নেবেন))


icallyতিহাসিকভাবে বেশ কয়েকটি সাধারণ মানদণ্ডে পরীক্ষা করা হয়নি - কারণ বিভাগটি সহজাত পুনরাবৃত্তি এবং দ্রুত করা শক্ত! এক্স 86 কমপক্ষে অংশ হিসেবে বাকি আছে div/ idivযা ইন্টেল Penryn, ব্রডওয়েলের এবং IceLake (উচ্চতর র্যাডিক্স হার্ডওয়্যার বিভাজনযন্ত্র) এ কিছু ভালোবাসা অর্জিত হয়েছে
পিটার Cordes

1
"শক্তি হ্রাস" সম্পর্কে আমার বোঝা হ'ল আপনি একটি লুপের একটি ভারী অপারেশনটিকে একটি একক হালকা অপারেশনের মাধ্যমে প্রতিস্থাপন করেন, যেমন x = i * constপ্রতিটি পুনরাবৃত্তির পরিবর্তে আপনি x += constপ্রতিটি পুনরাবৃত্তি করেন। আমি মনে করি না যে একটি একক গুণকে শিফট / অ্যাড লুপের পরিবর্তে শক্তি-হ্রাস বলা হবে। en.wikedia.org/wiki/… বলেছেন শব্দটি সম্ভবত এইভাবে ব্যবহার করা যেতে পারে তবে একটি নোট দিয়ে "এই উপাদানটি বিতর্কিত It
পিটার কর্ডেস

9

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

নিম্নলিখিত বাস্তবায়ন বিবেচনা করুন

int divisible_ui_p(unsigned int m, unsigned int a)
{
    while (m > a) {
        m += a;
        m >>= __builtin_ctz(m);
    }

    if (m == a) {
        return 1;
    }

    return 0;
}

এবং অ্যারে

unsigned int A[100000/2];
unsigned int M[100000-1];

for (unsigned int a = 1; a < 100000; a += 2) {
    A[a/2] = a;
}
for (unsigned int m = 1; m < 100000; m += 1) {
    M[m-1] = m;
}

যা হয় / ব্যবহার এলোমেলো নেই এলোমেলো ফাংশন।

বদলানো ছাড়া, ফলাফল এখনও আছে

| implementation     | time [secs] |
|--------------------|-------------|
| divisible_ui_p     |    8.56user |
| builtin % operator |   17.59user |

যাইহোক, আমি একবার এই অ্যারেগুলি পরিবর্তন করি, ফলাফলগুলি ভিন্ন are

| implementation     | time [secs] |
|--------------------|-------------|
| divisible_ui_p     |   31.34user |
| builtin % operator |   17.53user |
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.