it-swarm.asia

ما هو تأثير الطلب إذا ... آخر إذا كانت الجمل بالاحتمال؟

على وجه التحديد ، إذا كان لديّ سلسلة من عبارات if...else if ، وأنا أعرف بطريقة أو بأخرى مسبقًا الاحتمال النسبي الذي سيقيمه كل بيان بـ true ، ما مقدار الاختلاف في وقت التنفيذ الذي يصنعه لفرزها بترتيب الاحتمال؟ على سبيل المثال ، هل أفضّل هذا:

if (highly_likely)
  //do something
else if (somewhat_likely)
  //do something
else if (unlikely)
  //do something

الى هذا؟:

if (unlikely)
  //do something
else if (somewhat_likely)
  //do something
else if (highly_likely)
  //do something

يبدو من الواضح أن الإصدار المصنف سيكون أسرع ، ولكن من أجل سهولة القراءة أو وجود آثار جانبية ، قد نرغب في طلبها بطريقة غير مثالية. من الصعب أيضًا معرفة مدى أداء وحدة المعالجة المركزية بالتنبؤ بالفرع حتى تقوم بتشغيل الشفرة بالفعل.

لذا ، أثناء تجربة ذلك ، انتهيت إلى الإجابة على سؤالي الخاص بحالة معينة ، ومع ذلك أود سماع آراء/رؤى أخرى أيضًا.

هام: يفترض هذا السؤال أن عبارات if يمكن إعادة ترتيبها بشكل تعسفي دون أن يكون لها أي تأثيرات أخرى على سلوك البرنامج. في إجابتي ، الاختبارات الشرطية الثلاثة متبادلة ولا تنتج أي آثار جانبية. بالتأكيد ، إذا كان يجب تقييم البيانات بترتيب معين لتحقيق بعض السلوك المرغوب فيه ، فستكون مسألة الكفاءة موضع نقاش.

182
Carlton

كقاعدة عامة ، تفترض معظم وحدات المعالجة المركزية Intel - إن لم تكن كلها - أن الفروع الأمامية لا تؤخذ في المرة الأولى التي تراها فيها. انظر عمل جودبولت .

بعد ذلك ، ينتقل الفرع إلى ذاكرة التخزين المؤقت للتنبؤ بالفرع ، ويتم استخدام السلوك السابق لإبلاغ التنبؤ بالفرع في المستقبل.

لذلك في حلقة ضيقة ، سيكون تأثير misordering صغير نسبيا. سوف يتنبأ متنبئ الفرع بفروع الفروع الأكثر ترجيحًا ، وإذا كان لديك قدر غير قليل من العمل في الحلقة ، فإن الفروق الصغيرة لن تزيد كثيرًا.

في التعليمات البرمجية العامة ، فإن معظم المترجمين بشكل افتراضي (يفتقر إلى سبب آخر) سيطلبون رمز الجهاز المنتج تقريبًا بالطريقة التي طلبت بها في الكود. وبالتالي إذا كانت العبارات هي فروع للأمام عندما تفشل.

لذلك يجب عليك طلب فروعك بترتيب الاحتمال المتناقص للحصول على أفضل تنبؤ للفرع من "اللقاء الأول".

إن علامة الميكروفون الصغيرة التي يتم تشغيلها بإحكام عدة مرات على مجموعة من الشروط وتقوم بعمل تافه سوف تهيمن عليها تأثيرات صغيرة من عدد التعليمات وما شابه ذلك ، والقليل في طريق مشكلات التنبؤ النسبي للفرع. في هذه الحالة ، يجب عليك الملف الشخصي ، لأن قواعد الإبهام لن تكون موثوقة.

علاوة على ذلك ، ينطبق التمرين والعديد من التحسينات الأخرى على حلقات ضيقة صغيرة.

لذا ، في الكود العام ، ضع الكود الأكثر ترجيحًا ضمن كتلة if ، وسيؤدي ذلك إلى عدد أقل من توقعات الفروع غير المخزنة مؤقتًا. في الحلقات الضيقة ، اتبع القاعدة العامة للبدء ، وإذا كنت بحاجة إلى معرفة المزيد ، فلديك خيار صغير غير ملف التعريف.

بطبيعة الحال ، كل هذا يخرج من النافذة إذا كانت بعض الاختبارات أرخص بكثير من غيرها.

94
Yakk - Adam Nevraumont

قمت بإجراء الاختبار التالي حتى وقت تنفيذ كتلتين مختلفتين if...else if block ، واحدة مرتبة بترتيب الاحتمال ، والآخر مرتبة بترتيب عكسي:

#include <chrono>
#include <iostream>
#include <random>
#include <algorithm>
#include <iterator>
#include <functional>

using namespace std;

int main()
{
    long long sortedTime = 0;
    long long reverseTime = 0;

    for (int n = 0; n != 500; ++n)
    {
        //Generate a vector of 5000 random integers from 1 to 100
        random_device rnd_device;
        mt19937 rnd_engine(rnd_device());
        uniform_int_distribution<int> rnd_dist(1, 100);
        auto gen = std::bind(rnd_dist, rnd_engine);
        vector<int> Rand_vec(5000);
        generate(begin(Rand_vec), end(Rand_vec), gen);

        volatile int nLow, nMid, nHigh;
        chrono::time_point<chrono::high_resolution_clock> start, end;

        //Sort the conditional statements in order of increasing likelyhood
        nLow = nMid = nHigh = 0;
        start = chrono::high_resolution_clock::now();
        for (int& i : Rand_vec) {
            if (i >= 95) ++nHigh;               //Least likely branch
            else if (i < 20) ++nLow;
            else if (i >= 20 && i < 95) ++nMid; //Most likely branch
        }
        end = chrono::high_resolution_clock::now();
        reverseTime += chrono::duration_cast<chrono::nanoseconds>(end-start).count();

        //Sort the conditional statements in order of decreasing likelyhood
        nLow = nMid = nHigh = 0;
        start = chrono::high_resolution_clock::now();
        for (int& i : Rand_vec) {
            if (i >= 20 && i < 95) ++nMid;  //Most likely branch
            else if (i < 20) ++nLow;
            else if (i >= 95) ++nHigh;      //Least likely branch
        }
        end = chrono::high_resolution_clock::now();
        sortedTime += chrono::duration_cast<chrono::nanoseconds>(end-start).count();

    }

    cout << "Percentage difference: " << 100 * (double(reverseTime) - double(sortedTime)) / double(sortedTime) << endl << endl;
}

باستخدام MSVC2017 مع/O2 ، تظهر النتائج أن الإصدار الذي تم فرزه هو باستمرار حوالي 28٪ أسرع من الإصدار غير المصنف. في تعليق luk32 ، قمت أيضًا بتبديل ترتيب الاختبارين ، مما يحدث فرقًا ملحوظًا (22٪ مقابل 28٪). تم تشغيل الكود تحت ويندوز 7 على جهاز Intel Xeon E5-2697 v2. هذا بالطبع خاص بكل مشكلة ولا يجب تفسيره كإجابة قاطعة.

45
Carlton

لا يجب عليك ، إلا إذا كنت متأكدًا حقًا من تأثر النظام المستهدف. افتراضيا الذهاب عن طريق القراءة.

أشك بشدة في نتائجك. لقد قمت بتعديل مثالك قليلاً ، لذا فإن عكس التنفيذ أسهل. Ideone بالأحرى يظهر أن الترتيب العكسي أسرع ، وإن لم يكن كثيرًا. على أشواط معينة حتى هذا انقلبت في بعض الأحيان. أود أن أقول أن النتائج غير حاسمة. coliru تقارير لا يوجد فرق حقيقي كذلك. يمكنني التحقق من Exynos5422 CPU على بلدي xu4 odroid في وقت لاحق.

الشيء هو أن وحدات المعالجة المركزية الحديثة لها تنبئ فرع. يوجد الكثير من المنطق المخصص لإحضار البيانات والتعليمات بشكل مسبق ، كما أن وحدات المعالجة المركزية x86 الحديثة ذكية نوعًا ما ، عندما يتعلق الأمر بذلك. قد تكون بعض البنى الأقل حجماً مثل أرمينيا أو وحدات معالجة الرسومات عرضة لهذا. لكنه يعتمد بشكل كبير حقا على كل من النظام المترجم والهدف.

أود أن أقول إن تحسين ترتيب الفروع هش للغاية وسريع الزوال. افعل ذلك فقط كخطوة مضبوطة حقًا.

الشفرة:

#include <chrono>
#include <iostream>
#include <random>
#include <algorithm>
#include <iterator>
#include <functional>

using namespace std;

int main()
{
    //Generate a vector of random integers from 1 to 100
    random_device rnd_device;
    mt19937 rnd_engine(rnd_device());
    uniform_int_distribution<int> rnd_dist(1, 100);
    auto gen = std::bind(rnd_dist, rnd_engine);
    vector<int> Rand_vec(5000);
    generate(begin(Rand_vec), end(Rand_vec), gen);
    volatile int nLow, nMid, nHigh;

    //Count the number of values in each of three different ranges
    //Run the test a few times
    for (int n = 0; n != 10; ++n) {

        //Run the test again, but now sort the conditional statements in reverse-order of likelyhood
        {
          nLow = nMid = nHigh = 0;
          auto start = chrono::high_resolution_clock::now();
          for (int& i : Rand_vec) {
              if (i >= 95) ++nHigh;               //Least likely branch
              else if (i < 20) ++nLow;
              else if (i >= 20 && i < 95) ++nMid; //Most likely branch
          }
          auto end = chrono::high_resolution_clock::now();
          cout << "Reverse-sorted: \t" << chrono::duration_cast<chrono::nanoseconds>(end-start).count() << "ns" << endl;
        }

        {
          //Sort the conditional statements in order of likelyhood
          nLow = nMid = nHigh = 0;
          auto start = chrono::high_resolution_clock::now();
          for (int& i : Rand_vec) {
              if (i >= 20 && i < 95) ++nMid;  //Most likely branch
              else if (i < 20) ++nLow;
              else if (i >= 95) ++nHigh;      //Least likely branch
          }
          auto end = chrono::high_resolution_clock::now();
          cout << "Sorted:\t\t\t" << chrono::duration_cast<chrono::nanoseconds>(end-start).count() << "ns" << endl;
        }
        cout << endl;
    }
}
28
luk32

فقط 5 سنتات يبدو أن تأثير الطلب إذا كانت البيانات يجب أن تعتمد على:

  1. احتمالية كل بيان.

  2. عدد التكرارات ، لذلك يمكن للتنبؤ فرع ركلة.

  3. تلميحات برنامج التحويل المحتمل/المحتمل ، على سبيل المثال تخطيط الكود.

لاستكشاف هذه العوامل ، قمت بتقييم الوظائف التالية:

ordered_ifs ()

for (i = 0; i < data_sz * 1024; i++) {
    if (data[i] < check_point) // highly likely
        s += 3;
    else if (data[i] > check_point) // samewhat likely
        s += 2;
    else if (data[i] == check_point) // very unlikely
        s += 1;
}

reversed_ifs ()

for (i = 0; i < data_sz * 1024; i++) {
    if (data[i] == check_point) // very unlikely
        s += 1;
    else if (data[i] > check_point) // samewhat likely
        s += 2;
    else if (data[i] < check_point) // highly likely
        s += 3;
}

ordered_ifs_with_hints ()

for (i = 0; i < data_sz * 1024; i++) {
    if (likely(data[i] < check_point)) // highly likely
        s += 3;
    else if (data[i] > check_point) // samewhat likely
        s += 2;
    else if (unlikely(data[i] == check_point)) // very unlikely
        s += 1;
}

reversed_ifs_with_hints ()

for (i = 0; i < data_sz * 1024; i++) {
    if (unlikely(data[i] == check_point)) // very unlikely
        s += 1;
    else if (data[i] > check_point) // samewhat likely
        s += 2;
    else if (likely(data[i] < check_point)) // highly likely
        s += 3;
}

البيانات

يحتوي صفيف البيانات على أرقام عشوائية بين 0 و 100:

const int RANGE_MAX = 100;
uint8_t data[DATA_MAX * 1024];

static void data_init(int data_sz)
{
    int i;
        srand(0);
    for (i = 0; i < data_sz * 1024; i++)
        data[i] = Rand() % RANGE_MAX;
}

نتائج

النتائج التالية تخص Intel i5 @ 3،2 GHz و G ++ 6.3.0. الوسيطة الأولى هي check_point (أي الاحتمال في ٪٪ للرجح المحتمل للبيان) ، والوسيطة الثانية هي data_sz (أي عدد التكرارات).

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4                    4660 ns       4658 ns     150948
ordered_ifs/50/8                   25636 ns      25635 ns      27852
ordered_ifs/75/4                    4326 ns       4325 ns     162613
ordered_ifs/75/8                   18242 ns      18242 ns      37931
ordered_ifs/100/4                   1673 ns       1673 ns     417073
ordered_ifs/100/8                   3381 ns       3381 ns     207612
reversed_ifs/50/4                   5342 ns       5341 ns     126800
reversed_ifs/50/8                  26050 ns      26050 ns      26894
reversed_ifs/75/4                   3616 ns       3616 ns     193130
reversed_ifs/75/8                  15697 ns      15696 ns      44618
reversed_ifs/100/4                  3738 ns       3738 ns     188087
reversed_ifs/100/8                  7476 ns       7476 ns      93752
ordered_ifs_with_hints/50/4         5551 ns       5551 ns     125160
ordered_ifs_with_hints/50/8        23191 ns      23190 ns      30028
ordered_ifs_with_hints/75/4         3165 ns       3165 ns     218492
ordered_ifs_with_hints/75/8        13785 ns      13785 ns      50574
ordered_ifs_with_hints/100/4        1575 ns       1575 ns     437687
ordered_ifs_with_hints/100/8        3130 ns       3130 ns     221205
reversed_ifs_with_hints/50/4        6573 ns       6572 ns     105629
reversed_ifs_with_hints/50/8       27351 ns      27351 ns      25568
reversed_ifs_with_hints/75/4        3537 ns       3537 ns     197470
reversed_ifs_with_hints/75/8       16130 ns      16130 ns      43279
reversed_ifs_with_hints/100/4       3737 ns       3737 ns     187583
reversed_ifs_with_hints/100/8       7446 ns       7446 ns      93782

تحليل

1. الترتيب أمر مهم

بالنسبة لتكرار 4K واحتمال 100٪ تقريبًا لبيان محبوب للغاية ، يكون الفرق كبيرًا بنسبة 223٪:

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/100/4                   1673 ns       1673 ns     417073
reversed_ifs/100/4                  3738 ns       3738 ns     188087

بالنسبة للتكرار 4K واحتمال 50 ٪ من بيان محبوب للغاية ، الفرق هو حوالي 14 ٪:

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4                    4660 ns       4658 ns     150948
reversed_ifs/50/4                   5342 ns       5341 ns     126800

2. عدد التكرارات لا يهم

الفرق بين التكرارات 4K و 8K لـ (تقريبًا) احتمال 100٪ للبيان المحبوب للغاية هو حوالي مرتين (كما هو متوقع):

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/100/4                   1673 ns       1673 ns     417073
ordered_ifs/100/8                   3381 ns       3381 ns     207612

ولكن الفرق بين التكرارات 4K و 8K لاحتمال 50 ٪ من العبارة محبوب للغاية هو 5،5 مرات:

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4                    4660 ns       4658 ns     150948
ordered_ifs/50/8                   25636 ns      25635 ns      27852

لماذا هو كذلك؟ بسبب فرع التنبؤ يخطئ. هنا هو فرع يخطئ لكل حالة المذكورة أعلاه:

ordered_ifs/100/4    0.01% of branch-misses
ordered_ifs/100/8    0.01% of branch-misses
ordered_ifs/50/4     3.18% of branch-misses
ordered_ifs/50/8     15.22% of branch-misses

لذا ، في جهاز i5 الخاص بي ، يفشل جهاز تنبؤ الفروع بشكل مذهل بالنسبة للفروع غير المرجحة ومجموعات البيانات الكبيرة.

3. تلميحات مساعدة قليلا

بالنسبة لتكرارات 4K ، كانت النتائج أسوأ إلى حد ما بالنسبة لنسبة الاحتمال بنسبة 50٪ وأفضل إلى حد ما للاحتمال بنسبة 100٪ تقريبًا:

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/4                    4660 ns       4658 ns     150948
ordered_ifs/100/4                   1673 ns       1673 ns     417073
ordered_ifs_with_hints/50/4         5551 ns       5551 ns     125160
ordered_ifs_with_hints/100/4        1575 ns       1575 ns     437687

ولكن بالنسبة لتكرار 8K ، تكون النتائج دائمًا أفضل قليلاً:

---------------------------------------------------------------------
Benchmark                              Time           CPU Iterations
---------------------------------------------------------------------
ordered_ifs/50/8                   25636 ns      25635 ns      27852
ordered_ifs/100/8                   3381 ns       3381 ns     207612
ordered_ifs_with_hints/50/8        23191 ns      23190 ns      30028
ordered_ifs_with_hints/100/8        3130 ns       3130 ns     221205

لذا ، فإن التلميحات تساعد أيضًا ، ولكن قليلاً جدًا.

الاستنتاج العام هو: دائمًا ما يقيس الرمز ، لأن النتائج قد تفاجئ.

امل ان يساعد.

23
Andriy Berestovskyy

بناءً على بعض الإجابات الأخرى هنا ، يبدو أن الإجابة الحقيقية الوحيدة هي: هذا يعتمد . يعتمد ذلك على الأقل على ما يلي (وإن لم يكن بالضرورة بترتيب الأهمية):

  • الاحتمال النسبي لكل فرع. هذا هو السؤال الأصلي الذي تم طرحه. استنادًا إلى الإجابات الحالية ، يبدو أن هناك بعض الشروط التي يساعد في ظلها الترتيب حسب الاحتمال ، ولكن يبدو أن الأمر ليس كذلك دائمًا. إذا لم تكن الاحتمالات النسبية مختلفة تمامًا ، فمن غير المرجح أن تحدث فرقًا في أي ترتيب موجود فيها. ومع ذلك ، إذا حدث الشرط الأول بنسبة 99.999 ٪ من الوقت والشرط التالي هو جزء صغير مما تبقى ، فإنني نفترض أن وضع الأكثر احتمالا أولاً سيكون مفيداً من حيث التوقيت.
  • تكلفة حساب الشرط الحقيقي/الخاطئ لكل فرع. إذا كانت التكلفة الزمنية لاختبار الشروط مرتفعة حقًا لأحد الفروع مقابل الآخر ، فمن المحتمل أن يكون لذلك تأثير كبير على التوقيت والكفاءة. على سبيل المثال ، ضع في اعتبارك شرطًا يستغرق وحدة زمنية واحدة لحساب (على سبيل المثال ، التحقق من حالة متغير Boolean) مقابل شرط آخر يحتاج إلى عشرات أو مئات أو آلاف أو حتى ملايين الوحدات الزمنية لحساب (على سبيل المثال ، التحقق من محتويات ملف على القرص أو إجراء استعلام SQL معقدة مقابل قاعدة بيانات كبيرة). بافتراض أن الشفرة تتحقق من الشروط بالترتيب في كل مرة ، فمن المحتمل أن تكون الشروط الأسرع أولاً (ما لم تكن معتمدة على شروط أخرى تفشل أولاً).
  • المحول البرمجي/المترجم الشفوي قد تتضمن بعض المترجمات (أو المترجمين الفوريين) تحسينات من نوع آخر يمكن أن تؤثر على الأداء (وبعضها فقط موجود في حالة تحديد خيارات معينة أثناء التحويل البرمجي و/أو التنفيذ). لذلك ، ما لم تكن تقارن بين مجموعتين وتنفيذ تعليمات برمجية متطابقة على نفس النظام باستخدام نفس المحول البرمجي الدقيق حيث يكون الاختلاف الوحيد هو ترتيب الفروع المعنية ، فسوف يتعين عليك إعطاء مهلة للتغيرات في المحول البرمجي.
  • نظام التشغيل/الأجهزة كما ذكر luk32 و Yakk ، فإن وحدات المعالجة المركزية المختلفة لها تحسينات خاصة بها (كما تفعل أنظمة التشغيل). لذا فإن المعايير عرضة للتغير هنا مرة أخرى.
  • تواتر تنفيذ كتلة التعليمات البرمجية إذا كان نادراً ما يتم الوصول إلى الكتلة التي تحتوي على الفروع (على سبيل المثال ، مرة واحدة فقط أثناء بدء التشغيل) ، فمن المحتمل أن يكون الأمر ضعيفًا جدًا في ترتيب ترتيب الفروع. من ناحية أخرى ، إذا كان رمزك يبتعد بعيدًا عن كتلة الشفرة هذه أثناء جزء هام من الشفرة ، فقد يكون الطلب أمرًا مهمًا (حسب المعايير).

الطريقة الوحيدة لمعرفة اليقين هي قياس الحالة الخاصة بك ، ويفضل أن يكون ذلك على نظام مماثل (أو مشابه جدًا) للنظام المقصود الذي سيتم تشغيل الشفرة عليه أخيرًا. إذا كان القصد من ذلك هو العمل على مجموعة من الأنظمة المختلفة ذات الأجهزة المختلفة ، ونظام التشغيل ، وما إلى ذلك ، فمن الأفضل أن تقاس عبر أشكال متعددة لمعرفة أيهما أفضل. قد يكون من الجيد أيضًا تجميع التعليمات البرمجية بترتيب واحد على نوع واحد من النظام وترتيب آخر على نوع آخر من النظام.

قاعدة الإبهام الشخصية (بالنسبة لمعظم الحالات ، في حالة عدم وجود معيار مرجعي) هي الترتيب بناءً على:

  1. الشروط التي تعتمد على نتيجة الشروط السابقة ،
  2. تكلفة حساب الشرط ، ثم
  3. الاحتمال النسبي لكل فرع.
18
Ampersat

الطريقة التي عادةً ما أرى حلها لهذا الكود عالي الأداء هي الحفاظ على الترتيب الأكثر قابلية للقراءة ، لكن مع توفير تلميحات إلى المحول البرمجي. فيما يلي مثال واحد من Linux kernel :

if (likely(access_ok(VERIFY_READ, from, n))) {
    kasan_check_write(to, n);
    res = raw_copy_from_user(to, from, n);
}
if (unlikely(res))
    memset(to + (n - res), 0, res);

الافتراض هنا هو أن فحص الوصول سيمر ، وأنه لم يتم إرجاع أي خطأ في res. تحاول إعادة ترتيب أيٍّ من هذين الخيارين إذا كانت الجمل تخلط بين الشفرة فقط ، لكن وحدات الماكرو likely() و unlikely() تساعد في الواقع على القراءة من خلال الإشارة إلى الحالة الطبيعية وما هو الاستثناء.

يستخدم تطبيق Linux لتلك وحدات الماكرو ميزات خاصة بمجلس التعاون الخليجي . يبدو أن برنامج التحويل البرمجي clang و Intel C يدعم بناء الجملة نفسه ، لكن لا يحتوي MSVC على هذه الميزة .

12
jpa

يعتمد أيضًا على برنامج التحويل البرمجي والنظام الأساسي الذي تجمعه.

من الناحية النظرية ، فإن الحالة الأكثر ترجيحًا تجعل السيطرة تقفز إلى أدنى درجة ممكنة.

عادةً ما يكون الشرط الأكثر احتمالًا هو الأول:

if (most_likely) {
     // most likely instructions
} else …

تعتمد أسماء asm الأكثر شعبية على الفروع الشرطية التي تقفز عندما تكون الحالة true . من المحتمل أن تترجم شفرة C إلى مثل هذا الاسم الزائف:

jump to ELSE if not(most_likely)
// most likely instructions
jump to end
ELSE:
…

وذلك لأن القفزات تجعل وحدة المعالجة المركزية تلغي خط أنابيب التنفيذ والمماطلة لأن عداد البرنامج قد تغير (بالنسبة للمعماريات التي تدعم خطوط الأنابيب الشائعة بالفعل). ثم يتعلق الأمر بالمترجم ، الذي قد يقوم أو لا يطبق بعض التحسينات المعقدة حول وجود الشرط الإحصائي على الأرجح للحصول على عنصر التحكم مما يؤدي إلى حدوث قفزات أقل.

6
NoImaginationGuy

قررت إعادة تشغيل الاختبار على الجهاز الخاص بي باستخدام رمز Lik32. اضطررت لتغييره بسبب بلدي ويندوز أو المترجم التفكير عالية الدقة هو 1ms ، وذلك باستخدام

mingw32-g ++. exe -O3 -Wall -std = c ++ 11 -exexions -g

vector<int> Rand_vec(10000000);

حققت دول مجلس التعاون الخليجي نفس التحول على كلا الكودتين الأصليتين.

لاحظ أن الشرطين الأولين فقط يتم اختبارهما لأن الشرط الثالث يجب أن يكون دائمًا صحيحًا ، دول مجلس التعاون الخليجي هي نوع من شيرلوك هنا.

عكس

.L233:
        mov     DWORD PTR [rsp+104], 0
        mov     DWORD PTR [rsp+100], 0
        mov     DWORD PTR [rsp+96], 0
        call    std::chrono::_V2::system_clock::now()
        mov     rbp, rax
        mov     rax, QWORD PTR [rsp+8]
        jmp     .L219
.L293:
        mov     edx, DWORD PTR [rsp+104]
        add     edx, 1
        mov     DWORD PTR [rsp+104], edx
.L217:
        add     rax, 4
        cmp     r14, rax
        je      .L292
.L219:
        mov     edx, DWORD PTR [rax]
        cmp     edx, 94
        jg      .L293 // >= 95
        cmp     edx, 19
        jg      .L218 // >= 20
        mov     edx, DWORD PTR [rsp+96]
        add     rax, 4
        add     edx, 1 // < 20 Sherlock
        mov     DWORD PTR [rsp+96], edx
        cmp     r14, rax
        jne     .L219
.L292:
        call    std::chrono::_V2::system_clock::now()

.L218: // further down
        mov     edx, DWORD PTR [rsp+100]
        add     edx, 1
        mov     DWORD PTR [rsp+100], edx
        jmp     .L217

And sorted

        mov     DWORD PTR [rsp+104], 0
        mov     DWORD PTR [rsp+100], 0
        mov     DWORD PTR [rsp+96], 0
        call    std::chrono::_V2::system_clock::now()
        mov     rbp, rax
        mov     rax, QWORD PTR [rsp+8]
        jmp     .L226
.L296:
        mov     edx, DWORD PTR [rsp+100]
        add     edx, 1
        mov     DWORD PTR [rsp+100], edx
.L224:
        add     rax, 4
        cmp     r14, rax
        je      .L295
.L226:
        mov     edx, DWORD PTR [rax]
        lea     ecx, [rdx-20]
        cmp     ecx, 74
        jbe     .L296
        cmp     edx, 19
        jle     .L297
        mov     edx, DWORD PTR [rsp+104]
        add     rax, 4
        add     edx, 1
        mov     DWORD PTR [rsp+104], edx
        cmp     r14, rax
        jne     .L226
.L295:
        call    std::chrono::_V2::system_clock::now()

.L297: // further down
        mov     edx, DWORD PTR [rsp+96]
        add     edx, 1
        mov     DWORD PTR [rsp+96], edx
        jmp     .L224

لذلك هذا لا يخبرنا الكثير إلا أن الحالة الأخيرة لا تحتاج إلى فرع التنبؤ.

الآن جربت جميع التوليفات الـ 6 الخاصة بـ if's ، أعلى 2 هما العكس الأصلي وفرزها. ارتفاع> = 95 ، منخفض <20 ، منتصف 20-94 مع 10000000 تكرار لكل منهما.

high, low, mid: 43000000ns
mid, low, high: 46000000ns
high, mid, low: 45000000ns
low, mid, high: 44000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns

high, low, mid: 44000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 45000000ns

high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns

high, low, mid: 42000000ns
mid, low, high: 46000000ns
high, mid, low: 46000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 43000000ns

high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 44000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns

high, low, mid: 43000000ns
mid, low, high: 48000000ns
high, mid, low: 44000000ns
low, mid, high: 44000000ns
mid, high, low: 45000000ns
low, high, mid: 45000000ns

high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns

high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns

high, low, mid: 43000000ns
mid, low, high: 46000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 45000000ns
low, high, mid: 44000000ns

high, low, mid: 42000000ns
mid, low, high: 46000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 45000000ns
low, high, mid: 44000000ns

1900020, 7498968, 601012

Process returned 0 (0x0)   execution time : 2.899 s
Press any key to continue.

فلماذا الترتيب مرتفع ، منخفض ، متوسط ​​ثم أسرع (هامشي)

لأن أكثر ما يمكن التنبؤ به هو الأخير ، وبالتالي لا يتم تشغيله من خلال متنبئ الفرع.

          if (i >= 95) ++nHigh;               // most predictable with 94% taken
          else if (i < 20) ++nLow; // (94-19)/94% taken ~80% taken
          else if (i >= 20 && i < 95) ++nMid; // never taken as this is the remainder of the outfalls.

لذلك سيتم توقع الفروع التي اتخذت ، والتي اتخذت مع البقية

6٪ + (0.94 *) 20٪ مخطئون.

"مرتبة"

          if (i >= 20 && i < 95) ++nMid;  // 75% not taken
          else if (i < 20) ++nLow;        // 19/25 76% not taken
          else if (i >= 95) ++nHigh;      //Least likely branch

وسيتم التنبؤ الفروع مع عدم اتخاذها ، وليس اتخاذها وشرلوك.

25 ٪ + (0.75 *) 24 ٪ مخطئون

إعطاء فرق 18-23 ٪ (الفرق يقاس ~ 9 ٪) ولكن نحن بحاجة إلى حساب دورات بدلا من سوء تقدير ٪.

دعنا نفترض أن هناك 17 دورة من العقوبات التي أسيء تقديرها على وحدة المعالجة المركزية Nehalem الخاصة بي وأن كل عملية تدقيق تستغرق دورة واحدة لإصدارها (4-5 تعليمات) وأن الحلقة تأخذ دورة واحدة أيضًا. تبعيات البيانات هي العدادات ومتغيرات الحلقة ، ولكن بمجرد أن تكون الأخطاء الخاطئة خارجة عن الطريق فلن تؤثر على التوقيت.

لذلك بالنسبة لـ "العكس" ، نحصل على التوقيت (يجب أن تكون هذه هي الصيغة المستخدمة في هندسة الكمبيوتر: النهج الكمي IIRC).

mispredict*penalty+count+loop
0.06*17+1+1+    (=3.02)
(propability)*(first check+mispredict*penalty+count+loop)
(0.19)*(1+0.20*17+1+1)+  (= 0.19*6.4=1.22)
(propability)*(first check+second check+count+loop)
(0.75)*(1+1+1+1) (=3)
= 7.24 cycles per iteration

والشيء نفسه بالنسبة لـ "مرتبة"

0.25*17+1+1+ (=6.25)
(1-0.75)*(1+0.24*17+1+1)+ (=.25*7.08=1.77)
(1-0.75-0.19)*(1+1+1+1)  (= 0.06*4=0.24)
= 8.26

(8.26-7.24) /8.26 = 13.8٪ مقابل ~ 9٪ مقاسة (قريبة من المقاسة!؟!).

إذن ما هو واضح من البروتوكول الاختياري غير واضح.

مع هذه الاختبارات ، ستكون الاختبارات الأخرى التي تحتوي على تعليمات برمجية أكثر تعقيدًا أو مزيدًا من تبعيات البيانات مختلفة بالتأكيد ، لذا قم بقياس حالتك.

غير تغيير ترتيب الاختبار النتائج ، لكن قد يكون ذلك بسبب محاذاة مختلفة من بداية الحلقة والتي يجب أن تكون محاذاة 16 بايت على جميع وحدات المعالجة المركزية Intel Intel الأحدث ولكن ليس في هذه الحالة.

4
Surt

وضعها في أي ترتيب منطقي تريد. بالتأكيد ، قد يكون الفرع أبطأ ، ولكن لا ينبغي أن يكون التفريع هو الجزء الأكبر من العمل الذي يقوم به جهاز الكمبيوتر الخاص بك.

إذا كنت تعمل على جزء هام من الأداء من التعليمات البرمجية ، فبالتأكيد استخدم الترتيب المنطقي والتحسين الموجه لملف التعريف وغير ذلك من التقنيات ، لكن بالنسبة إلى الكود العام ، أعتقد أنه اختيارًا أفضل من حيث الأسلوب.

3
Jack

إذا كنت تعرف بالفعل الاحتمال النسبي لبيان if-else ، فمن الأفضل لغرض الأداء استخدام الطريقة التي تم فرزها ، حيث إنها ستقوم بفحص شرط واحد فقط (الشرط الحقيقي).

بطريقة غير مصنفة سوف يقوم المترجم بفحص جميع الشروط دون داع وسيستغرق بعض الوقت.

2
aditya rawat