වර්ගීකරණය කරන ලද අරාවක් සැකසීම, වර්ගීකරණය නොකළ අරාවක් සැකසීමට වඩා වේගවත් වන්නේ ඇයි?


24459

මෙන්න ඉතා සුවිශේෂී හැසිරීමක් පෙන්වන C ++ කේත කැබැල්ලක්. කිසියම් අමුතු හේතුවක් නිසා, දත්ත ආශ්චර්යමත් ලෙස වර්ග කිරීම මඟින් කේතය හය ගුණයකින් වේගවත් කරයි:

#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);
    }
}

සමාන නමුත් අඩු ආන්තික ප්‍රති .ලයක් සමඟ.


මගේ පළමු සිතුවිල්ල වූයේ වර්ග කිරීම මඟින් දත්ත හැඹිලිය තුළට ගෙන එනු ඇති බවයි, නමුත් පසුව මම සිතුවේ අරාව ජනනය වී ඇති නිසා එය කෙතරම් මෝඩකමක්ද යන්නයි.

  • මොනවද වෙන්නේ?
  • වර්ගීකරණය කරන ලද අරාවක් සැකසීම, වර්ගීකරණය නොකළ අරාවක් සැකසීමට වඩා වේගවත් වන්නේ ඇයි?

කේතය සමහර ස්වාධීන යෙදුම් සාරාංශ කරයි, එබැවින් ඇණවුම වැදගත් නොවේ.



16
Ach සචින්වර්මා මගේ හිස මුදුනේ සිට: 1) කොන්දේසි සහිත පියවරයන් භාවිතා කිරීමට තරම් ජේවීඑම් අවසානයේ දක්ෂ විය හැකිය. 2) කේතය මතකයෙන් බැඳී ඇත. 200M CPU හැඹිලියට සරිලන තරම් විශාලය. එබැවින් කාර්ය සාධනය අතු බෙදීම වෙනුවට මතක කලාප පළල මගින් බාධා කරනු ඇත.
අද්භූත

12
@ ගුප්ත, 2 ක් පමණ). මම සිතුවේ පුරෝකථන වගුව රටාවන් නිරීක්ෂණය කරයි (එම රටාව සඳහා පරීක්ෂා කරන ලද සත්‍ය විචල්‍යයන් නොසලකා) සහ ඉතිහාසය මත පදනම්ව පුරෝකථන ප්‍රතිදානය වෙනස් කරයි. කරුණාකර මට හේතුවක් දෙන්න, සුපිරි විශාල පෙළක් ශාඛා අනාවැකි වලින් ප්‍රයෝජන නොගන්නේ මන්ද?
සචින් වර්මා

15
Ach සචින්වර්මා එය සිදු කරයි, නමුත් අරාව එතරම් විශාල වූ විට ඊටත් වඩා විශාල සාධකයක් ක්‍රියාත්මක වේ - මතක කලාප පළල. මතකය සමතලා නොවේ . මතකයට ප්‍රවේශ වීම ඉතා මන්දගාමී වන අතර සීමිත කලාප පළලක් ඇත. දේවල් ඕනෑවට වඩා සරල කිරීම සඳහා, නිශ්චිත කාලයක් තුළ CPU සහ මතකය අතර මාරු කළ හැකි බයිට් විශාල ප්‍රමාණයක් ඇත. මෙම ප්‍රශ්නයේ ඇති කේතය වැනි සරල කේතය වැරදි අනාවැකි මගින් මන්දගාමී වුවද එම සීමාවට පැමිණෙනු ඇත. 32768 (128KB) පෙළක් සමඟ මෙය සිදු නොවේ, මන්ද එය CPU හි L2 හැඹිලියට ගැලපේ.
අද්භූත

13
BranchScope නමින් නව ආරක්ෂක දෝෂයක් තිබේ: cs.ucr.edu/~nael/pubs/asplos18.pdf
Veve

Answers:


31809

ඔබ ශාඛා අනාවැකි අසාර්ථක වීමේ ගොදුරක් .


ශාඛා පුරෝකථනය යනු කුමක්ද?

දුම්රිය මංසන්ධියක් සලකා බලන්න:

රේල් පාර හන්දියක් පෙන්වන රූපය රූපය මෙකනිස්මෝ, විකිමීඩියා කොමන්ස් හරහා. යටතේ භාවිතා CC-BY-SA 3.0 බලපත්රය.

දැන් තර්කය සඳහා, මෙය 1800 ගණන්වල - දිගු දුර හෝ ගුවන් විදුලි සන්නිවේදනයට පෙර යැයි සිතමු.

ඔබ හන්දියක ක්‍රියාකරු වන අතර දුම්රියක් පැමිණෙන බව ඔබට ඇසේ. එය යා යුත්තේ කුමන මාර්ගයටදැයි ඔබට අදහසක් නැත. ඔබ දුම්රිය නතර කරන්නේ රියදුරුට අවශ්‍ය දිශාව කුමක්දැයි විමසීමටයි. ඉන්පසු ඔබ ස්විචය සුදුසු පරිදි සකසා ගන්න.

දුම්රිය බරින් යුක්ත වන අතර විශාල අවස්ථිති බවක් ඇත. එබැවින් ඔවුන් ආරම්භ කිරීමට හා මන්දගාමී වීමට සදහටම ගත වේ.

මීට වඩා හොඳ ක්‍රමයක් තිබේද? ඔබ සිතන්නේ දුම්රිය යන්නේ කුමන දිශාවටද කියා!

  • ඔබ නිවැරදිව අනුමාන කළහොත්, එය දිගටම පවතී.
  • ඔබ වැරදියට අනුමාන කළහොත්, කපිතාන්වරයා නවත්වනු ඇත, උපස්ථ වේ, සහ ස්විචය පෙරළීමට කෑගසනු ඇත. එවිට එය අනෙක් මාර්ගය නැවත ආරම්භ කළ හැකිය.

ඔබ සෑම විටම නිවැරදිව අනුමාන කරන්නේ නම් , දුම්රිය කිසි විටෙකත් නැවැත්විය යුතු නැත.
ඔබ බොහෝ විට වැරදි යැයි අනුමාන කරන්නේ නම් , දුම්රිය නැවැත්වීමට, උපස්ථ කිරීමට සහ නැවත ආරම්භ කිරීමට බොහෝ කාලයක් ගත කරනු ඇත.


If-statement එකක් සලකා බලන්න: ප්‍රොසෙසර් මට්ටමින් එය ශාඛා උපදෙස් වේ:

If ප්‍රකාශයක් අඩංගු සම්පාදිත කේතයේ තිර රුව

ඔබ ප්‍රොසෙසරයක් වන අතර ඔබට ශාඛාවක් පෙනේ. එය යන්නේ කුමන දිශාවටදැයි ඔබට අදහසක් නැත. ඔයා මොකද කරන්නේ? ඔබ ක්‍රියාත්මක කිරීම නවතා පෙර උපදෙස් සම්පූර්ණ වන තෙක් රැඳී සිටින්න. එවිට ඔබ නිවැරදි මාවතේ ඉදිරියට යන්න.

නවීන සකසනයන් සංකීර්ණ වන අතර දිගු නල මාර්ග ඇත. එබැවින් ඔවුන් "උණුසුම් වීමට" සහ "වේගය අඩු කිරීමට" සදහටම ගනී.

මීට වඩා හොඳ ක්‍රමයක් තිබේද? ශාඛාව යන්නේ කුමන දිශාවටදැයි ඔබ අනුමාන කරයි!

  • ඔබ නිවැරදිව අනුමාන කළහොත්, ඔබ දිගටම ක්‍රියාත්මක කරයි.
  • ඔබ වැරදි යැයි අනුමාන කළහොත්, ඔබට නල මාර්ගය ගලවා නැවත ශාඛාවට පෙරළීමට අවශ්‍යය. එවිට ඔබට අනෙක් මාර්ගය නැවත ආරම්භ කළ හැකිය.

ඔබ සෑම විටම නිවැරදිව අනුමාන කරන්නේ නම් , ක්‍රියාත්මක කිරීම කිසි විටෙකත් නතර කළ යුතු නොවේ.
ඔබ බොහෝ විට වැරදි යැයි අනුමාන කරන්නේ නම් , ඔබ බොහෝ වේලාවක් ඇණහිටීම, පෙරළීම සහ නැවත ආරම්භ කිරීම සඳහා වැය කරයි.


මෙය ශාඛා පුරෝකථනයයි. දුම්රියට ධජයකින් දිශාව සං signal ා කළ හැකි බැවින් එය හොඳම ප්‍රතිසමයක් නොවන බව මම පිළිගනිමි. නමුත් පරිගණක වලදී, අවසාන මොහොත දක්වා ශාඛාවක් යන්නේ කුමන දිශාවටද යන්න ප්‍රොසෙසරය නොදනී.

ඉතින්, දුම්රිය උපස්ථ වී අනෙක් මාර්ගයෙන් බැස යා යුතු වාර ගණන අවම කිරීමට ඔබ උපායමාර්ගිකව අනුමාන කරන්නේ කෙසේද? ඔබ අතීත ඉතිහාසය දෙස බලයි! දුම්රිය 99% ක් වමට ගියහොත් ඔබ අනුමාන කරන්නේ වමට ය. එය විකල්ප නම්, ඔබ ඔබේ අනුමාන වෙනස් කරයි. එය සෑම තුන් වරක්ම එක් මාර්ගයකට ගියහොත්, ඔබ අනුමාන කරන්නේ එයමයි ...

වෙනත් වචන වලින් කිවහොත්, ඔබ රටාවක් හඳුනාගෙන එය අනුගමනය කිරීමට උත්සාහ කරයි. ශාඛා අනාවැකි කරුවන් ක්‍රියා කරන ආකාරය මෙය අඩු හෝ වැඩි ය.

බොහෝ යෙදුම්වල හොඳින් හැසිරෙන ශාඛා ඇත. එබැවින් නවීන ශාඛා අනාවැකි කරන්නන් සාමාන්‍යයෙන්> 90% පහර අනුපාත ලබා ගනී. නමුත් හඳුනාගත නොහැකි රටාවන් නොමැති අනපේක්ෂිත ශාඛාවන්ට මුහුණ දෙන විට, ශාඛා අනාවැකි කරන්නන් පාහේ නිෂ් .ල ය.

වැඩිදුර කියවීම: විකිපීඩියාව පිළිබඳ "ශාඛා අනාවැකි" ලිපිය .


ඉහළින් ඉඟි කර ඇති පරිදි, වැරදිකරු මෙය නම්-ප්‍රකාශය:

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

දත්ත 0 සහ 255 අතර ඒකාකාරව බෙදා හරින බව සැලකිල්ලට ගන්න. දත්ත වර්ග කළ විට, දළ වශයෙන් පළමු පුනරාවර්තනයන් if-statement වෙත ඇතුල් නොවනු ඇත. ඊට පසු, ඔවුන් සියල්ලන්ම if-statement වෙත ඇතුළු වනු ඇත.

ශාඛාව අඛණ්ඩව එකම දිශාවකට බොහෝ වාරයක් යන බැවින් මෙය ශාඛා අනාවැකි කරුවාට ඉතා මිත්‍රශීලී ය. සරල සංතෘප්ත කවුන්ටරයක් ​​පවා ශාඛාව දිශාව මාරු කිරීමෙන් පසු පුනරාවර්තන කිහිපයක් හැර නිවැරදිව පුරෝකථනය කරයි.

ඉක්මන් දෘශ්‍යකරණය:

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)

කෙසේ වෙතත්, දත්ත සම්පුර්ණයෙන්ම අහඹු ලෙස සිදු වූ විට, ශාඛා අනාවැකි නිෂ් less ල වනු ඇත, එයට අහඹු දත්ත පුරෝකථනය කළ නොහැකි බැවිනි. මේ අනුව බොහෝ විට වැරදි අනාවැකි 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];

මෙය ශාඛාව ඉවත් කර එය බිට්වේස් මෙහෙයුම් මගින් ප්‍රතිස්ථාපනය කරයි.

(මෙම අනවසරයෙන් මුල් if-statement ට සමාන නොවන බව සලකන්න. නමුත් මේ අවස්ථාවේ දී, එය සියලු ආදාන අගයන් සඳහා වලංගු වේ data[].)

මිණුම් සලකුණු: මූලික i7 920 @ 3.5 GHz

සී ++ - විෂුවල් ස්ටුඩියෝ 2010 - x64 නිකුතුව

//  Branch - Random
seconds = 11.777

//  Branch - Sorted
seconds = 2.352

//  Branchless - Random
seconds = 2.564

//  Branchless - Sorted
seconds = 2.587

ජාවා - නෙට්බීන්ස් 7.1.1 ජේඩීකේ 7 - x64

//  Branch - Random
seconds = 10.93293813

//  Branch - Sorted
seconds = 5.643797077

//  Branchless - Random
seconds = 3.113581453

//  Branchless - Sorted
seconds = 3.186068823

නිරීක්ෂණ:

  • ශාඛාව සමඟ: වර්ග කළ හා වර්ග නොකළ දත්ත අතර විශාල වෙනසක් ඇත.
  • හැක් සමඟ: වර්ග කළ සහ වර්ග නොකළ දත්ත අතර වෙනසක් නැත.
  • C ++ නඩුවේදී, දත්ත වර්ග කරන විට හැක් යනු ශාඛාවට වඩා මන්දගාමී වේ.

සාමාන්‍ය ඇඟිල්ලේ රීතියක් නම් විවේචනාත්මක ලූපවල දත්ත මත යැපීම වළක්වා ගැනීමයි (මෙම උදාහරණය වැනි).


යාවත්කාලීන කිරීම:

  • X64 සමඟ -O3හෝ -ftree-vectorizeමත ඇති GCC 4.6.1 මගින් කොන්දේසි සහිත පියවරක් ජනනය කළ හැකිය. එබැවින් වර්ග කළ හා වර්ග නොකළ දත්ත අතර වෙනසක් නැත - දෙකම වේගවත් ය.

    (නැතහොත් තරමක් වේගයෙන්: දැනටමත්-හරිම නඩුව සඳහා, cmovමන්දගාමී, විශේෂයෙන්ම පමණක් ගල්ෆ් තීරනාත්මක මාවතේ තබන එය නම් වෙනුවට කළ හැකි add, විශේෂයෙන් ඉන්ටෙල් මත Broadwell එහිදී පෙර cmov: 2 චක්රය පමාවක් ඇති මන්දගාමී -O2 වඩා gcc ප්රශස්තිකරණය ධජය -O3 වර්ගයන් කේතය )

  • VC ++ 2010 ට මෙම ශාඛාව සඳහා කොන්දේසි සහිත චලනයන් උත්පාදනය කළ නොහැක /Ox.

  • ඉන්ටෙල් සී ++ කම්පයිලර් (අයිසීසී) 11 ආශ්චර්යමත් දෙයක් කරයි. එය වළළු දෙකක් අන්තර් හුවමාරු එමගින් කලින් කිව නොහැකි ශාඛා පිටත පුඩුවක් කිරීමට එසවීම,. එබැවින් එය වැරදි අනාවැකි වලට ප්‍රතිශක්තීකරණයක් පමණක් නොව, VC ++ සහ GCC මගින් ජනනය කළ හැකි ඕනෑම දෙයකට වඩා දෙගුණයක් වේගවත් වේ! වෙනත් වචන වලින් කිවහොත්, මිණුම් දණ්ඩ පරාජය කිරීම සඳහා අයිසීසී ටෙස්ට් ලූපයෙන් ප්‍රයෝජන ගත්තේය ...

  • ඔබ ඉන්ටෙල් සම්පාදකයාට ශාඛා රහිත කේතය ලබා දෙන්නේ නම්, එය පිටත දකුණට දෛශික කරයි ... එය ශාඛාව මෙන් වේගවත් වේ (ලූප් අන්තර් හුවමාරුව සමඟ).

පරිණත නවීන සම්පාදකයින්ට පවා කේත ප්‍රශස්තිකරණය කිරීමේ හැකියාව අනුව වෙනස් විය හැකි බව මෙයින් පෙනේ ...


256
මෙම පසු විපරම් ප්‍රශ්නය දෙස බලන්න: stackoverflow.com/questions/11276291/… ඉන්ටෙල් සම්පාදකයා පිටත ලූපය මුළුමනින්ම ඉවත් කිරීමට ආසන්නව පැමිණියේය.
අද්භූත

25
Y ගුප්ත විද්‍යාව දුම්රිය / සම්පාදකයා වැරදි මාර්ගයකට ඇතුළු වී ඇති බව දැන ගන්නේ කෙසේද?
onmyway133

26
obobe: ධූරාවලි මතක ව්‍යුහයන් අනුව, හැඹිලි මිස් එකක වියදම කුමක් දැයි කිව නොහැක. එය L1 හි මඟ හැරී මන්දගාමී L2 වලින් විසඳා ගත හැකිය, නැතහොත් L3 හි මග හැරී පද්ධති මතකයේ නිරාකරණය වේ. කෙසේ වෙතත්, කිසියම් විකාර හේතුවක් නිසා මෙම හැඹිලි මිස් අනේවාසික පිටුවක මතකය තැටියෙන් පටවනු ලැබුවහොත්, ඔබට හොඳ කරුණක් තිබේ ... මතකයට අවුරුදු 25-30 අතර කාලයක් තුළ මිලි තත්පර පරාසය තුළ ප්‍රවේශ වීමේ වේලාවක් නොමැත. ;)
ඇන්ඩන් එම්. කෝල්මන්

21
නවීන සකසනයක කාර්යක්ෂම වන කේත ලිවීමේ නියමය : ඔබේ වැඩසටහන ක්‍රියාත්මක කිරීම වඩාත් විධිමත් (අඩු අසමාන) බවට පත් කරන සෑම දෙයක්ම එය වඩාත් කාර්යක්ෂම කිරීමට නැඹුරු වනු ඇත. ශාඛා අනාවැකි නිසා මෙම උදාහරණයේ වර්ග කිරීම මෙම බලපෑම ඇති කරයි. හැඹිලි නිසා ප්‍රවේශ ප්‍රදේශය (දුර and ත අහඹු ප්‍රවේශයන්ට වඩා) මෙම බලපෑම ඇති කරයි.
ලුට්ස් ප්‍රීචෙල්ට්

22
And සන්දීප් ඔව්. සකසනයන්ට තවමත් ශාඛා අනාවැකි ඇත. කිසිවක් වෙනස් වී ඇත්නම්, එය සම්පාදකයින් ය. වර්තමානයේදී, ඔවුන් අයිසීසී සහ ජීසීසී (-O3 යටතේ) කළ දේ කිරීමට වැඩි ඉඩක් ඇති බව මම විශ්වාස කරමි - එනම් ශාඛාව ඉවත් කරන්න. මෙම ප්‍රශ්නය කෙතරම් ඉහළ මට්ටමක පවතීද යන්න මත, මෙම ප්‍රශ්නයේ නඩුව විශේෂයෙන් හැසිරවීමට සම්පාදකයින් යාවත්කාලීන කර ඇති බව පෙනේ. අනිවාර්යයෙන්ම SO වෙත අවධානය යොමු කරන්න. සති 3 ක් තුළ GCC යාවත්කාලීන කරන ලද මෙම ප්‍රශ්නය මත එය සිදුවිය . එය මෙහි සිදු නොවන්නේ මන්දැයි මම නොදනිමි.
ගුප්ත

4088

ශාඛා පුරෝකථනය.

වර්ග කළ අරාවකින්, කොන්දේසිය data[c] >= 128පළමුව falseඅගයන් පෙළක් සඳහා වන අතර trueපසුව සියලු පසු අගයන් සඳහා වේ. එය අනාවැකි කීම පහසුය. වර්ගීකරණය නොකළ අරාවකින්, ඔබ අතු බෙදීමේ පිරිවැය ගෙවයි.


105
විවිධ රටා සහිත වර්ග කළ අරා එදිරිව අරා මත ශාඛා පුරෝකථනය වඩා හොඳින් ක්‍රියා කරයිද? උදාහරණයක් ලෙස, අරාව සඳහා -> {10, 5, 20, 10, 40, 20, ... the රටාවේ සිට අරාවෙහි ඊළඟ අංගය 80 වේ. ශාඛා අනාවැකි මගින් මේ ආකාරයේ අරාව වේගවත් කළ හැකිද? රටාව අනුගමනය කරන්නේ නම් ඊළඟ මූලද්‍රව්‍යය මෙහි 80 ද? නැතහොත් එය සාමාන්‍යයෙන් උපකාර කරන්නේ වර්ග කළ අරා වලට පමණක්ද?
ඇඩම් ෆ්‍රීමන්

133
ඉතින් මූලික වශයෙන් මම බිග්-ඕ ගැන සාම්ප්‍රදායිකව ඉගෙන ගත් සියල්ල ජනේලයෙන් පිටත තිබේද? අතු බෙදීමේ පිරිවැයට වඩා වර්ග කිරීමේ පිරිවැයක් දැරීමට වඩා හොඳද?
අග්‍රිම් පාතක්

133
G අග්‍රිම් පාතක් එය රඳා පවතී. විශාල ආදානයක් නොමැති නිසා, ඉහළ සංකීර්ණත්වයක් සහිත ඇල්ගොරිතමයක් අඩු සංකීර්ණත්වයක් සහිත ඇල්ගොරිතමයකට වඩා වේගවත් වේ. කඩාවැටීමේ ලක්ෂ්‍යය කොතැනදැයි අනාවැකි කීම දුෂ්කර විය හැකිය. එසේම, මෙය සසඳා බලන්න , ප්‍රදේශය වැදගත් ය. බිග්-ඕ වැදගත්, නමුත් එය කාර්ය සාධනය සඳහා වන එකම නිර්ණායකය නොවේ.
ඩැනියෙල් ෆිෂර්

65
ශාඛා පුරෝකථනය සිදු වන්නේ කවදාද? අරාව වර්ග කර ඇති බව භාෂාව දැන ගන්නේ කවදාද? [1,2,3,4,5, ... 998,999,1000, 3, 10001, 10002] වැනි අරා වල තත්වය ගැන මම සිතමි. මෙම අපැහැදිලි 3 ධාවන කාලය වැඩි කරයිද? එය වර්ගීකරණය නොකළ අරාව පවතින තාක් කල් පවතිනු ඇත්ද?
පිලිප් බාර්ටුසි

63
IlFilipBartuzi ශාඛා පුරෝකථනය ප්‍රොසෙසරය තුළ, භාෂා මට්ටමට වඩා පහළින් සිදු වේ (නමුත් භාෂාව සම්පාදකයාට විය හැකි දේ පැවසීමට ක්‍රම ඉදිරිපත් කරයි, එබැවින් සම්පාදකයාට එයට ගැලපෙන කේත විමෝචනය කළ හැකිය). ඔබේ උදාහරණයේ දී, ඇණවුමෙන් පිටත 3 ශාඛා-වැරදි පුරෝකථනයකට තුඩු දෙනු ඇත (සුදුසු තත්වයන් සඳහා, 3 ක් 1000 ට වඩා වෙනස් ප්‍රති result ලයක් ලබා දෙයි), එම නිසා එම අරාව සැකසීම නැනෝ තත්පර දුසිමක් හෝ සියයක් ගත වේ. වර්ග කළ අරාව කිසිසේත්ම කැපී පෙනෙන්නේ නැත. කාලය සඳහා වැය වන මුදල මම අධික ලෙස වැරදි අනාවැකි පළ කිරීම, 1000 කට එක් වැරදි අනාවැකියක් එතරම් නොවේ.
ඩැනියෙල් ෆිෂර්

3312

දත්ත වර්ග කරන විට කාර්ය සාධනය විශාල ලෙස වැඩිදියුණු වීමට හේතුව, ගුප්ත විද්‍යාවේ පිළිතුරෙහි මනාව පැහැදිලි කර ඇති පරිදි ශාඛා පුරෝකථන ද penalty ුවම ඉවත් කිරීමයි .

දැන් අපි කේතය දෙස බැලුවහොත්

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

මෙම විශේෂිත if... else...ශාඛාවේ අර්ථය කොන්දේසියක් සෑහීමකට පත්වන විට යමක් එකතු කිරීම බව අපට සොයාගත හැකිය . ශාඛා මෙම වර්ගයේ පහසුවෙන් බවට පරිවර්තනය කළ හැකි කොන්දේසි පියවර : කොන්දේසි පියවර උපදෙස් සම්පාදනය කරන අතර ඒ ප්රකාශය, cmovlක දී, x86පද්ධතිය. ශාඛාව සහ ඒ අනුව විභව ශාඛා පුරෝකථන ද penalty ුවම ඉවත් කරනු ලැබේ.

මේ Cඅනුව C++, කොන්දේසිගත චලන උපදෙස් වලට කෙලින්ම (කිසිදු ප්‍රශස්තිකරණයකින් තොරව) සම්පාදනය කරන ප්‍රකාශය x86තෘතීය ක්‍රියාකරු ... ? ... : ...වේ. එබැවින් අපි ඉහත ප්‍රකාශය සමාන එකකට නැවත ලියන්නෙමු:

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

කියවීමේ හැකියාව පවත්වා ගනිමින් අපට වේගවත් කිරීමේ සාධකය පරීක්ෂා කළ හැකිය.

Intel Core i7 -2600K @ 3.4 GHz සහ Visual Studio 2010 Release Mode හි මිණුම් ලකුණ (ගුප්ත විද්‍යාවෙන් පිටපත් කරන ලද ආකෘතිය):

x86

//  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

ප්රති result ලය බහු පරීක්ෂණ වලදී ශක්තිමත් වේ. ශාඛා ප්‍රති result ලය අනාවැකි කිව නොහැකි වූ විට අපට විශාල වේගයක් ලැබෙනු ඇත, නමුත් එය පුරෝකථනය කළ හැකි විට අපි ටිකක් දුක් විඳිමු. ඇත්ත වශයෙන්ම, කොන්දේසි සහිත පියවරක් භාවිතා කරන විට, දත්ත රටාව නොසලකා කාර්ය සාධනය සමාන වේ.

දැන් 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අතර, පුරෝකථනය කරන ලද ප්‍රති result ලය නිවැරදි නොවේ නම් සැලකිය යුතු කාර්යසාධන ද penalty ුවමක් ලැබෙනු ඇත.

ඉතින් කොන්දේසි සහිත පියවරක් වඩා හොඳ වන්නේ ඇයි?

සාමාන්‍ය x86සකසනයක දී, උපදෙස් ක්‍රියාත්මක කිරීම අදියර කිහිපයකට බෙදා ඇත. දළ වශයෙන්, විවිධ අදියරයන් සමඟ කටයුතු කිරීමට අපට විවිධ දෘඩාංග තිබේ. එබැවින් නව එකක් ආරම්භ කිරීම සඳහා එක් උපදෙස් අවසන් වන තෙක් අප බලා සිටිය යුතු නැත. මෙය නල මාර්ගකරණය ලෙස හැඳින්වේ .

ශාඛා නඩුවකදී, පහත දැක්වෙන උපදෙස් පූර්වයෙන් තීරණය වේ, එබැවින් අපට නල මාර්ගගත කිරීම කළ නොහැක. අපට බලා සිටීමට හෝ අනාවැකි කීමට සිදුවේ.

කොන්දේසි පියවර නඩුවේ, ක්රියාත්මක කොන්දේසි පියවර උපදෙස් විවිධ අවස්ථා බෙදා ඇත, නමුත් මීට පෙර අවස්ථා වැනි Fetchසහ Decodeපෙර උපදෙස් ප්රතිඵලයක් මත රඳා පවතී නැත; ප්‍රති .ලය අවශ්‍ය වන්නේ අවසාන අදියරවලට පමණි. මේ අනුව, එක් උපදෙස් ක්‍රියාත්මක කිරීමේ වේලාවෙන් කොටසක් අපි බලා සිටිමු. පුරෝකථනය කිරීම පහසු වන විට කොන්දේසිගත චලනය අනුවාදය ශාඛාවට වඩා මන්දගාමී වන්නේ මේ නිසා ය.

පොත පරිගණක පද්ධති: ක්රමලේඛකයෙක් ගේ දර්ශනය, දෙවන සංස්කරණය විස්තර මෙම පැහැදිලි කරයි. ශාඛා පුරෝකථනය සහ වැරදි අනාවැකි දඩ මුදල් සඳහා විශේෂ ප්‍රතිකාරයක් සඳහා ඔබට කොන්දේසි සහිත චලන උපදෙස් සඳහා 3.6.6 වගන්තිය , ප්‍රොසෙසර් ගෘහ නිර්මාණ ශිල්පය සඳහා 4 වන පරිච්ඡේදය සහ 5.11.2 වගන්තිය පරීක්ෂා කළ හැකිය .

සමහර විට, සමහර නවීන සම්පාදකයින්ට අපගේ කේතය වඩා හොඳ කාර්ය සාධනයක් සහිතව එකලස් කිරීම සඳහා ප්‍රශස්තිකරණය කළ හැකිය, සමහර විට සමහර සම්පාදකයින්ට එය කළ නොහැක (ප්‍රශ්න කේතය විෂුවල් ස්ටුඩියෝ හි ස්වදේශීය සම්පාදකය භාවිතා කරයි). අනාවැකි කිව නොහැකි විට ශාඛාව සහ කොන්දේසිගත චලනය අතර කාර්ය සාධන වෙනස දැන ගැනීම, තත්වය ඉතා සංකීර්ණ වූ විට වඩා හොඳ කාර්ය සාධනයක් සහිත කේත ලිවීමට අපට උපකාරී වේ.


7
@ BlueRaja-DannyPflughoeft මෙය ප්‍රශස්තිකරණය නොකළ අනුවාදයයි. සම්පාදකයා ත්‍රිමාණ ක්‍රියාකරු ප්‍රශස්තිකරණය කර නැත, එය පරිවර්තනය කරන්න. ප්‍රමාණවත් ප්‍රශස්තිකරණ මට්ටමක් ලබා දෙන්නේ නම් GCC හට ප්‍රශස්තිකරණය කළ හැකිය, කෙසේ වෙතත්, මෙය කොන්දේසි සහිත චලනයක බලය පෙන්වන අතර අතින් ප්‍රශස්තිකරණය වෙනසක් කරයි.
WiSaGaN

100
@WiSaGaN කේතය කිසිවක් පෙන්වන්නේ නැත, මන්ද ඔබේ කේත කොටස් දෙක එකම යන්ත්‍ර කේතයට සම්පාදනය වන බැවිනි. ඔබේ උදාහරණයේ ඇති if ප්‍රකාශය ඔබගේ උදාහරණයේ ඇති ටෙරෙනරියට වඩා වෙනස් ය යන අදහස මිනිසුන්ට නොලැබීම විවේචනාත්මකව වැදගත් ය. ඔබගේ අවසාන ඡේදයේ ඇති සමානකම් ඔබ සතුව ඇති බව සත්‍යයකි, නමුත් අනෙක් උදාහරණය හානිකර බව මකා නොදමයි.
ජස්ටින් එල්.

55
IWiSaGaN නොමඟ යවන -O0උදාහරණය ඉවත් කිරීමට සහ ඔබේ ටෙස්ට් කේස් දෙකෙහි ප්‍රශස්තිකරණය කළ asm හි වෙනස පෙන්වීමට ඔබ ඔබේ පිළිතුර වෙනස් කළහොත් මගේ පසුබෑම අනිවාර්යයෙන්ම ඉහළ යයි .
ජස්ටින් එල්.

56
@UpAndAdam පරීක්ෂණය සිදුවන මොහොතේදී, ඉහළ ප්‍රශස්තිකරණ මට්ටමක් නියම කිරීමේදී පවා VS2010 හට මුල් ශාඛාව කොන්දේසි සහිත පියවරක් ලෙස ප්‍රශස්තිකරණය කළ නොහැක.
WiSaGaN

9
මෙම තෘතීය ක්‍රියාකරු උපක්‍රමය ජාවා සඳහා අලංකාර ලෙස ක්‍රියා කරයි. මිස්ටික්ගේ පිළිතුර කියවීමෙන් පසු, මම කල්පනා කළේ ජාවා සතුව -O3 ට සමාන කිසිවක් නොමැති බැවින් ව්‍යාජ ශාඛා අනාවැකි වළක්වා ගැනීම සඳහා ජාවාට කුමක් කළ හැකිද යන්නයි. තෘතීය ක්‍රියාකරු: 2.1943s සහ මුල්: 6.0303s.
කිං චියුන්ග්

2272

මෙම කේතයට කළ හැකි තවත් ප්‍රශස්තිකරණයන් පිළිබඳව ඔබ කුතුහලයෙන් සිටී නම්, මෙය සලකා බලන්න:

මුල් පුඩුවෙන් පටන් ගෙන:

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
Ic මයිකල් - මෙම උදාහරණය ඇත්ත වශයෙන්ම ලූප්-ආක්‍රමණික එසවීම (LIH) ප්‍රශස්තිකරණයට උදාහරණයක් වන අතර , ලූප් හුවමාරුව නොවේ . මෙම අවස්ථාවෙහිදී, මුළු අභ්‍යන්තර iපුඩුවක්ම බාහිර ලූපයෙන් ස්වාධීන වන අතර එම නිසා පිටත ලූපයෙන් එසවිය හැකිය, එවිට ප්‍රති result ලය සරලවම එක් ඒකකයකට වඩා වැඩි වේ = 1e5. එය අවසාන ප්‍රති result ලයට කිසිදු වෙනසක් නොකරයි, නමුත් මට අවශ්‍ය වූයේ මෙය නිතර නිතර පිටුවක් වන බැවින් වාර්තාව කෙළින්ම සැකසීමටයි.
යයර් ඇල්ට්මන්

54
ලූප මාරු කිරීමේ සරල ස්වරූපයෙන් නොතිබුණද, ifමෙම අවස්ථාවෙහිදී අභ්‍යන්තරය බවට පරිවර්තනය කළ හැකිය: sum += (data[j] >= 128) ? data[j] * 100000 : 0;සම්පාදකයාට අඩු කිරීමට cmovgeහෝ සමාන කිරීමට හැකි විය හැකිය .
ඇලෙක්ස් නෝර්ත්-කීස්

43
පිටත ලූපය යනු අභ්‍යන්තර ලූපය විසින් ගත කරන කාලය පැතිකඩට ප්‍රමාණවත් තරම් විශාල කිරීමයි. ඉතින් ඇයි ඔබ ලූප් හුවමාරුව. අවසානයේදී, එම ලූපය කෙසේ හෝ ඉවත් කරනු ලැබේ.
saurabheights

34
aura සෞබයිට්ස්: වැරදි ප්‍රශ්නය: සම්පාදකයා ලූප මාරු නොවන්නේ ඇයි? මයික්‍රොබෙන්ච්මාර්ක් අමාරුයි;)
මැතිව් එම්.

1885

CPU හි ශාඛා-පුරෝකථනය කරන්නාට ගැටළු සහගත කේත හඳුනා ගැනීමේ ක්‍රම ගැන අපගෙන් සමහරු උනන්දු වන බවට සැකයක් නැත. Valgrind මෙවලමෙහි cachegrindශාඛා-පුරෝකථන සිමියුලේටරයක් ​​ඇත, එය --branch-sim=yesධජය භාවිතා කර සක්‍රීය කර ඇත . පිටත ලූප ගණන 10000 දක්වා අඩු කර සම්පාදනය කර මෙම ප්‍රශ්නයේ උදාහරණ හරහා එය ක්‍රියාත්මක g++කිරීමෙන් මෙම ප්‍රති results ල ලැබේ :

වර්ග කර ඇත:

==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 ක් ඇති කරයි.


විකල්පයක් ලෙස, ලිනක්ස් හි ඔබට එකම කාර්යය ඉටු කිරීම සඳහා කාර්ය සාධන කවුන්ටර උප පද්ධතිය භාවිතා කළ හැකිය, නමුත් දේශීය කාර්ය සාධනය සමඟ CPU කවුන්ටර භාවිතා කරන්න.

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% මිස් වලට වඩා හොඳින් කරන්නේ කෙසේද?
TallBrian

128
@ tall.b.lo: 25% සියලු ශාඛා වේ - ඇත දෙකක් කම්බියක් තුළ ශාඛා, එකක් data[c] >= 128(ඔබට යෝජනා කරමු ලෙස 50% ක මිස් අනුපාතය ඇති) සහ පුඩුවක් තත්ත්වය සඳහා එක් c < arraySize~ 0% මිස් අනුපාතය ඇති .
කැෆේ

1341

මම මේ ප්‍රශ්නය සහ එහි පිළිතුරු කියවා බැලුවෙමි, පිළිතුරක් නැති බව මට හැඟේ.

ශාඛා අනාවැකි තුරන් කිරීම සඳහා පොදු ක්‍රමයක් වන්නේ කළමනාකරණ භාෂාවලින් විශේෂයෙන් හොඳ ලෙස ක්‍රියා කිරීමට මා සොයාගෙන ඇති අතර එය ශාඛාවක් භාවිතා කරනවා වෙනුවට වගු සෙවීමකි (මම මේ අවස්ථාවේ දී එය අත්හදා බලා නැතත්).

මෙම ප්‍රවේශය පොදුවේ ක්‍රියාත්මක වන්නේ නම්:

  1. එය කුඩා මේසයක් වන අතර එය ප්‍රොසෙසරයේ හැඹිලිගත වීමට ඉඩ ඇත
  2. ඔබ දේවල් තරමක් තද පුඩුවක් තුළ ධාවනය කරන අතර / හෝ ප්‍රොසෙසරයට දත්ත පූර්ව පැටවිය හැක.

පසුබිම සහ ඇයි

ප්‍රොසෙසර් දෘෂ්ටි කෝණයකින්, ඔබේ මතකය මන්දගාමී වේ. වේගයෙහි වෙනසට වන්දි ගෙවීම සඳහා, ඔබේ ප්‍රොසෙසරය තුළට (L1 / L2 හැඹිලිය) හැඹිලි කිහිපයක් සාදා ඇත. ඒ නිසා ඔබ ඔබේ හොඳ ගණනය කිරීම් කරන බව සිතන්න, ඔබට මතක කොටසක් අවශ්‍ය බව හදුනා ගන්න. ප්‍රොසෙසරයට එහි 'ලෝඩ්' ක්‍රියාකාරිත්වය ලැබෙනු ඇති අතර මතක කොටස හැඹිලියට පටවනු ලැබේ - ඉන්පසු ඉතිරි ගණනය කිරීම් කිරීමට හැඹිලිය භාවිතා කරයි. මතකය සාපේක්ෂව මන්දගාමී බැවින් මෙම 'භාරය' ඔබේ වැඩසටහන මන්දගාමී කරයි.

ශාඛා අනාවැකි මෙන්, මෙය පෙන්ටියම් ප්‍රොසෙසර තුළ ප්‍රශස්තිකරණය කරන ලදි: ප්‍රොසෙසරය අනාවැකි පළ කරන්නේ එයට දත්ත කැබැල්ලක් පැටවිය යුතු බවත්, මෙහෙයුම සැබවින්ම හැඹිලියට පහර දීමට පෙර එය හැඹිලියට පැටවීමට උත්සාහ කරන බවත්ය. අප දැනටමත් දැක ඇති පරිදි, ශාඛා අනාවැකි සමහර විට දරුණු ලෙස වැරදී යයි - නරකම අවස්ථාවෙහිදී ඔබ ආපසු ගොස් මතක බරක් එනතෙක් බලා සිටිය යුතුය, එය සදහටම ගතවනු ඇත ( වෙනත් වචන වලින් කිවහොත්: ශාඛා අනාවැකි අසමත් වීම නරක ය, මතකය ශාඛා අනාවැකි අසමත් වීමෙන් පසු පැටවීම භයානක ය! ).

වාසනාවකට මෙන්, මතක ප්‍රවේශ රටාව පුරෝකථනය කළ හැකි නම්, ප්‍රොසෙසරය එහි වේගවත් හැඹිලියෙහි පටවනු ඇති අතර සියල්ල හොඳින් වේ.

අප දැනගත යුතු පළමු දෙය නම් කුඩා දේ කුමක්ද ? කුඩා වීම සාමාන්‍යයෙන් වඩා හොඳ වුවත්, නියමාකාර නීතියක් වන්නේ <= 4096 බයිට් ප්‍රමාණයේ බැලීමේ වගු වලට ඇලී සිටීමයි. ඉහළ සීමාවක් ලෙස: ඔබගේ බැලීමේ වගුව 64K ට වඩා විශාල නම් එය නැවත සලකා බැලීම වටී.

මේසයක් තැනීම

එබැවින් අපට කුඩා වගුවක් සෑදිය හැකි බව අපි හදුනා ගතිමු. මීළඟට කළ යුත්තේ බැලීමේ කාර්යයක් ලබා ගැනීමයි. බැලීමේ කාර්යයන් සාමාන්‍යයෙන් මූලික සංඛ්‍යා කිහිපයක් භාවිතා කරන කුඩා කාර්යයන් වේ (සහ, හෝ, xor, මාරුව, එකතු කිරීම, ඉවත් කිරීම සහ සමහර විට ගුණ කිරීම). බැලීමේ ශ්‍රිතය මඟින් ඔබේ ආදානය ඔබේ වගුවේ යම් ආකාරයක 'අද්විතීය යතුරකට' පරිවර්තනය කිරීමට ඔබට අවශ්‍යය, එවිට ඔබට එය කිරීමට අවශ්‍ය සියලු වැඩවලට පිළිතුරු ලබා දේ.

මෙම අවස්ථාවේ දී:> = 128 යන්නෙන් අපට වටිනාකම තබා ගත හැකිය, <128 යන්නෙන් අදහස් කරන්නේ අප එයින් මිදීමයි. එය කිරීමට ඇති පහසුම ක්‍රමය වන්නේ 'AND' භාවිතා කිරීමයි: අප එය තබා ගන්නේ නම්, අපි සහ එය 7FFFFFFF සමඟ; අපට එය ඉවත් කිරීමට අවශ්‍ය නම්, අපි සහ එය 0 සමඟ. 128 යනු 2 ක බලයක් බව ද සැලකිල්ලට ගන්න - එබැවින් අපට ඉදිරියට ගොස් පූර්ණ සංඛ්‍යා 32768/128 වගුවක් සාදා එය එක් ශුන්‍යයකින් සහ විශාල ප්‍රමාණයක් පුරවන්න 7FFFFFFFF ගේ.

කළමනාකරණය කළ භාෂා

කළමනාකරණ භාෂාවලින් මෙය හොඳින් ක්‍රියාත්මක වන්නේ මන්දැයි ඔබ සිතනු ඇත. සියල්ලට පසු, කළමනාකරණ භාෂාවන් අරා වල මායිම් ශාඛාවක් සමඟ පරීක්ෂා කර බලා ඔබ අවුල් නොවන බව සහතික කරයි ...

හොඳයි, හරියටම නොවේ ... :-)

කළමනාකරණ භාෂාවන් සඳහා මෙම ශාඛාව ඉවත් කිරීම සඳහා සෑහෙන වැඩ කොටසක් කර ඇත. උදාහරණයක් වශයෙන්:

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

මෙම අවස්ථාවේ දී, මායිම් කොන්දේසිය කිසි විටෙකත් පහර නොදෙන බව සම්පාදකයාට පැහැදිලිය. අවම වශයෙන් මයික්‍රොසොෆ්ට් JIT සම්පාදකයා (නමුත් මම බලාපොරොත්තු වන්නේ ජාවා ඒ හා සමාන දේ කරයි) මෙය දැක චෙක්පත සම්පූර්ණයෙන්ම ඉවත් කරනු ඇත. වාව්, ඒ කියන්නේ ශාඛාවක් නැහැ. ඒ හා සමානව, එය වෙනත් පැහැදිලි අවස්ථා සමඟ කටයුතු කරනු ඇත.

ඔබ කළමණාකරන භාෂාවලින් බැලීම් සමඟ කරදරයට පත්වුවහොත් - ප්‍රධාන දෙය & 0x[something]FFFනම් මායිම් පරීක්ෂාව පුරෝකථනය කළ හැකි වන පරිදි ඔබේ බැලීමේ කාර්යයට එකතු කිරීමයි - එය වේගයෙන් ගමන් කිරීම නැරඹීමයි.

මෙම නඩුවේ ප්රති result ලය

// 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 හි තරමක් භාවිතා කරයි. graphics.stanford.edu/~seander/bithacks.html
atlaste

36
පොදුවේ බැලීමේ වගු වේගවත් විය හැකි නමුත් මෙම විශේෂිත තත්වය සඳහා ඔබ පරීක්ෂණ පවත්වා තිබේද? ඔබේ කේතයේ ඔබට තවමත් ශාඛා තත්වයක් ඇත, දැන් එය බැලීමේ වගු උත්පාදනය කරන කොටස වෙත ගෙන යනු ලැබේ. ඔබට තවමත් ඔබේ පරිපූර්ණ තල්ලුව ලැබෙන්නේ නැත
සයින් රිස්වි

38
Ain සැබවින්ම ඔබට දැන ගැනීමට අවශ්‍ය නම් ... ඔව්: ශාඛාව සමඟ තත්පර 15 ක් සහ මගේ අනුවාදය සමඟ 10 ක්. කෙසේ වෙතත්, එය දෙයාකාරයෙන්ම දැන ගැනීම ප්‍රයෝජනවත් තාක්‍ෂණයකි.
atlaste

42
ඇයි නැත්තේ sum += lookup[data[j]]කොහෙද lookup256 ඇතුළත් කිරීම් සමග මාලාවක්, පළමු අය ශුන්ය වීම හා පසුගිය අය දර්ශකය සමාන වෙන්නේ කොහොමද?
ක්‍රිස් වැන්ඩර්මොටන්

1200

අරාව වර්ග කරන විට 0 සිට 255 දක්වා දත්ත බෙදා හරින බැවින්, පුනරාවර්තනවල පළමු භාගය පමණ if-ප්‍රස්ථාරයට ඇතුල් නොවනු ඇත ( ifප්‍රකාශය පහතින් බෙදා ඇත).

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

ප්‍රශ්නය වන්නේ: ඉහත ප්‍රකාශය වර්ග කරන ලද දත්ත වලදී මෙන් ඇතැම් අවස්ථාවලදී ක්‍රියාත්මක නොවීමට හේතුව කුමක්ද? මෙන්න "ශාඛා අනාවැකි" පැමිණේ. ශාඛා පුරෝකථනය යනු ඩිජිටල් පරිපථයකි, if-then-elseමෙය ස්ථිරවම දැන ගැනීමට පෙර ශාඛාවක් (උදා: ව්‍යුහයක්) යන්නේ කුමන දිශාවටදැයි අනුමාන කිරීමට උත්සාහ කරයි . ශාඛා අනාවැකි කරන්නාගේ අරමුණ වන්නේ උපදෙස් නල මාර්ගයේ ගලායාම වැඩි දියුණු කිරීමයි. ඉහළ effective ලදායී කාර්ය සාධනයක් ලබා ගැනීම සඳහා ශාඛා අනාවැකි කරුවන් තීරණාත්මක කාර්යභාරයක් ඉටු කරයි!

එය වඩා හොඳින් වටහා ගැනීම සඳහා අපි බංකුවක් සලකුණු කරමු

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
OoMooingDuck 'එයට වෙනසක් සිදු නොවනු ඇත - එම අගය ඕනෑම දෙයක් විය හැකි නමුත් එය තවමත් මෙම එළිපත්තෙහි සීමාවන් තුළ පවතිනු ඇත. ඔබ දැනටමත් සීමාවන් දන්නා විට අහඹු අගයක් පෙන්වන්නේ ඇයි? සම්පුර්ණත්වය උදෙසා ඔබට එකක් පෙන්විය හැකි බව මම එකඟ වුවද, 'එහි විපාකය සඳහා'.
cst1992

24
st cst1992: මේ මොහොතේ ඔහුගේ මන්දගාමී වේලාව TTFFTTFFTTFF ය, එය මගේ මිනිස් ඇසට පෙනෙන පරිදි තරමක් පුරෝකථනය කළ හැකිය. අහඹු ලෙස සහජයෙන්ම අනාවැකි කිව නොහැකි බැවින් එය තවමත් මන්දගාමී විය හැකි අතර මෙහි පෙන්වා ඇති සීමාවන්ට පිටින්. OTOH, සමහර විට TTFFTTFF ව්යාධිජනක නඩුවට හොඳින් පහර දෙයි. ඔහු අහඹු ලෙස වේලාව නොපෙන්වා ඇති බැවින් පැවසිය නොහැක.
මූයිං තාරා

21
OoMooingDuck මිනිස් ඇසකට, "TTFFTTFFTTFF" යනු පුරෝකථනය කළ හැකි අනුක්‍රමයකි, නමුත් අප මෙහි කතා කරන්නේ CPU තුළ ගොඩනගා ඇති ශාඛා අනාවැකි කරුවන්ගේ හැසිරීමයි. ශාඛා පුරෝකථනය AI මට්ටමේ රටා හඳුනා ගැනීම නොවේ; එය ඉතා සරලයි. ඔබ විකල්ප ශාඛා පමණක් කරන විට එය හොඳින් පුරෝකථනය නොකරයි. බොහෝ කේත වල, ශාඛා සෑම විටම පාහේ එකම ආකාරයකින් ගමන් කරයි; දහස් වාරයක් ක්‍රියාත්මක කරන ලූපයක් සලකා බලන්න. ලූපයේ අවසානයේ ඇති ශාඛාව ලූපයේ ආරම්භයට 999 වතාවක් ආපසු යයි, ඉන්පසු දහස වතාව වෙනස් දෙයක් කරයි. ඉතා සරල ශාඛා පුරෝකථනයක් සාමාන්‍යයෙන් හොඳින් ක්‍රියාත්මක වේ.
steveha

18
@steveha: මම හිතන්නේ ඔබ CPU ශාඛා අනාවැකි ක්‍රියා කරන ආකාරය ගැන උපකල්පන කරන අතර මම එම ක්‍රමවේදය සමඟ එකඟ නොවෙමි. එම ශාඛා පුරෝකථනය කෙතරම් දියුණුදැයි මම නොදනිමි, නමුත් එය ඔබට වඩා බොහෝ දියුණු යැයි මම සිතමි. ඔබ බොහෝ විට හරි, නමුත් මිනුම් අනිවාර්යයෙන්ම හොඳයි.
මූයිං තාරා

5
@steveha: ද්වි-මට්ටමේ අනුවර්තන අනාවැකි කරුවාට කිසිදු ගැටළුවක් නොමැතිව TTFFTTFF රටාවට අගුළු දැමිය හැකිය. "මෙම අනාවැකි ක්‍රමයේ ප්‍රභේද බොහෝ නවීන මයික්‍රොප්‍රොසෙසර වල භාවිතා වේ". දේශීය ශාඛා අනාවැකි සහ ගෝලීය ශාඛා පුරෝකථනය පදනම් වී ඇත්තේ මට්ටමේ අනුවර්තන අනාවැකි මත ය. "ගෝලීය ශාඛා අනාවැකි AMD ප්‍රොසෙසර වලද, ඉන්ටෙල් පෙන්ටියම් එම්, කෝර්, කෝර් 2 සහ සිල්වර්මොන්ට් මත පදනම් වූ පරමාණු සකසනයන්හිද භාවිතා වේ" තවද එකඟතා අනාවැකි, දෙමුහුන් අනාවැකි, වක්‍ර පැනීම් පිළිබඳ පුරෝකථනය එම ලැයිස්තුවට එක් කරන්න. ලූප් පුරෝකථනය අගුළු නොදමනු ඇත, නමුත් 75% ක් පහර දෙයි. එය අගුළු දැමිය නොහැකි 2 ක් පමණි
මූයිං තාරා

1126

ශාඛා පුරෝකථන දෝෂ වළක්වා ගත හැකි එක් ක්‍රමයක් නම් බැලීමේ වගුවක් තැනීම සහ දත්ත භාවිතා කර එය සුචිගත කිරීමයි. ස්ටෙෆාන් ඩි බ ru යින් ඔහුගේ පිළිතුරෙන් ඒ ගැන සාකච්ඡා කළේය.

නමුත් මේ අවස්ථාවේ දී, අගයන් [0, 255] පරාසයේ පවතින බව අපි දනිමු. අපි සැලකිලිමත් වන්නේ සාරධර්ම> = 128 ගැන පමණි. එයින් අදහස් කරන්නේ අපට වටිනාකමක් අවශ්‍යද නැද්ද යන්න අපට පවසන තනි බිට් එකක් පහසුවෙන් උකහා ගත හැකි බවයි: මාරුවීමෙන් දත්ත දකුණු බිටු 7 ට, අපට ඉතිරිව ඇත්තේ බිට් 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];

මෙම කේතය එකතු කිරීම් වලින් අඩක් නාස්ති කරන නමුත් කිසි විටෙකත් ශාඛා අනාවැකි අසමත් නොවේ. සත්‍ය නම් ප්‍රකාශයක් සහිත අනුවාදයට වඩා අහඹු දත්ත මත එය ඉතා වේගවත් වේ.

නමුත් මගේ පරීක්ෂණයේදී, පැහැදිලි බැලීමේ වගුවක් මීට වඩා තරමක් වේගවත් විය, බොහෝ විට බැලීමේ වගුවකට සුචිගත කිරීම බිට් මාරුවට වඩා මඳක් වේගවත් විය. මෙයින් පෙන්නුම් කරන්නේ මගේ කේතය lutසැකසීමේ සහ බැලීමේ වගුව භාවිතා කරන ආකාරයයි (සිතාගත නොහැකි ලෙස කේතයේ "බැලීමේ වගුව" ලෙස හැඳින්වේ ). මෙන්න C ++ කේතය:

// 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];

මෙන්න මෙම කේතයට සබැඳියක්: රතු කළු ගහ මන්දිරයේ , සදාකාලිකව Confuzzled


29
හරි, ඔබට බිට් කෙලින්ම භාවිතා කර ගුණ කළ හැකිය ( data[c]>>7- මෙහි කොතැනක හෝ සාකච්ඡා කෙරේ); මම හිතාමතාම මෙම විසඳුම අතහැර දැමුවෙමි, නමුත් ඇත්ත වශයෙන්ම ඔබ නිවැරදිය. කුඩා සටහනක්: බැලීමේ වගු සඳහා නියමය නම් එය 4KB ට ගැලපේ නම් (හැඹිලි නිසා), එය ක්‍රියාත්මක වනු ඇත - වගුව හැකි තරම් කුඩා කරන්න. කළමනාකරණ භාෂාවන් සඳහා මම එය 64KB වෙත තල්ලු කරමි, C ++ සහ C වැනි පහත් මට්ටමේ භාෂාවන් සඳහා, මම නැවත සලකා බලනු ඇත (එය මගේ අත්දැකීම පමණි). එතැන් සිට typeof(int) = 4, මම උපරිම බිටු 10 ට ඇලී සිටීමට උත්සාහ කරමි.
atlaste

17
මම හිතන්නේ 0/1 අගය සමඟ සුචිගත කිරීම බොහෝ විට පූර්ණ සංඛ්‍යා ගුණනයකට වඩා වේගවත් වනු ඇත, නමුත් කාර්ය සාධනය සැබවින්ම තීරණාත්මක නම් ඔබ එය පැතිකඩ කළ යුතුය. හැඹිලි පීඩනය වළක්වා ගැනීම සඳහා කුඩා විමසුම් වගු අත්‍යවශ්‍ය බව මම එකඟ වෙමි, නමුත් පැහැදිලිවම ඔබට විශාල හැඹිලියක් තිබේ නම් ඔබට විශාල බැලීමේ වගුවක් ලබා ගත හැකිය, එබැවින් 4KB යනු දැඩි රීතියකට වඩා මාපට ඇඟිල්ලකි. මම හිතන්නේ ඔබ අදහස් කළේ sizeof(int) == 4? 32-බිට් සඳහා එය සත්‍ය වේ. මගේ අවුරුදු දෙකක පැරණි ජංගම දුරකථනයේ 32KB L1 හැඹිලියක් ඇත, එබැවින් 4K බැලීමේ මේසයක් පවා ක්‍රියා කරයි, විශේෂයෙන් බැලීමේ අගයන් int වෙනුවට බයිට් එකක් නම්.
steveha

12
සමහර විට මට යමක් මග jහැරී ඇති නමුත් ඔබේ 0 හෝ 1 ක්‍රමයට සමානව ඔබ jඅරාව සුචිගත කිරීම භාවිතා කරනවාට වඩා එය එකතු කිරීමට පෙර ඔබේ අගය ගුණ නොකරන්නේ ඇයි (සමහර විට ඒ 1-jවෙනුවට ගුණ කළ යුතුය j)
රිචඩ් ටින්ගල්

6
@steveha ගුණ කිරීම වේගවත් විය යුතුය, මම එය ඉන්ටෙල් පොත්වල බැලීමට උත්සාහ කළෙමි, නමුත් එය සොයාගත නොහැකි විය ... කෙසේ හෝ මිණුම් සලකුණු කිරීම ද මට එම ප්‍රති result ලය මෙහි ලබා දෙයි.
atlaste

10
@steveha PS: කළ හැකි තවත් පිළිතුරක් වනුයේ int c = data[j]; sum += c & -(c >> 7);කිසිදු ගුණ කිරීමක් අවශ්‍ය නොවන බවයි.
atlaste

1022

වර්ග කළ අවස්ථාවෙහිදී, සාර්ථක ශාඛා අනාවැකි හෝ ශාඛා රහිත සංසන්දනාත්මක උපක්‍රමයක් මත යැපීමට වඩා හොඳ දෙයක් ඔබට කළ හැකිය: ශාඛාව සම්පූර්ණයෙන්ම ඉවත් කරන්න.

ඇත්ත වශයෙන්ම, අරාව සමාන්තර කලාපයක 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 සහිත සාම්පල 16384) :-)


23
sum= 3137536- දක්ෂයි. එය පැහැදිලිවම ප්‍රශ්නයේ කාරණය නොවේ. පුදුම සහගත කාර්ය සාධන ලක්ෂණ පැහැදිලි කිරීම පිළිබඳ ප්‍රශ්නය පැහැදිලිවම වේ. std::partitionඒ වෙනුවට කිරීම එකතු කිරීම වටී යැයි මම කියමි std::sort. සත්‍ය ප්‍රශ්නය ලබා දී ඇති කෘතිම මිණුම් ලකුණට වඩා වැඩි ය.
sehe

12
EdDeadMG: මෙය ඇත්ත වශයෙන්ම දී ඇති යතුරක් සඳහා වන සම්මත ද්වි පරමාණුක සෙවුම නොව, කොටස් කිරීමේ දර්ශකය සෙවීමකි; එයට එක් පුනරාවර්තනයකට තනි සංසන්දනයක් අවශ්‍ය වේ. නමුත් මෙම කේතය මත රඳා නොසිටින්න, මම එය පරීක්ෂා කර නැත. සහතික කළ නිවැරදි ක්‍රියාත්මක කිරීමක් ගැන ඔබ උනන්දු නම්, මට දන්වන්න.
යෙව්ස් ඩවුස්ට්

832

ඉහත හැසිරීම සිදුවන්නේ ශාඛා පුරෝකථනය නිසාය.

ශාඛා අනාවැකි අවබෝධ කර ගැනීම සඳහා පළමුව උපදෙස් නල මාර්ගය තේරුම් ගත යුතුය :

ඕනෑම උපදෙස් සමාන්තරව විවිධ පියවරයන් ක්‍රියාත්මක කළ හැකි වන පරිදි ඕනෑම උපදෙස් පියවර අනුපිළිවෙලකට බෙදා ඇත. මෙම තාක්ෂණය උපදෙස් නල මාර්ගයක් ලෙස හැඳින්වෙන අතර නවීන ප්‍රොසෙසරවල ප්‍රති put ල වැඩි කිරීමට මෙය භාවිතා කරයි. මෙය වඩා හොඳින් තේරුම් ගැනීමට කරුණාකර විකිපීඩියාවේ මෙම උදාහරණය බලන්න .

සාමාන්‍යයෙන් නවීන සකසනයන්ට තරමක් දිගු නල මාර්ග ඇත, නමුත් පහසුව සඳහා අපි මෙම පියවර 4 පමණක් සලකා බලමු.

  1. IF - මතකයෙන් උපදෙස් ලබා ගන්න
  2. හැඳුනුම්පත - උපදෙස් විකේතනය කරන්න
  3. EX - උපදෙස් ක්‍රියාත්මක කරන්න
  4. WB - CPU ලේඛනයට නැවත ලියන්න

උපදෙස් 2 ක් සඳහා පොදුවේ 4-අදියර නල මාර්ගය. පොදුවේ අදියර 4 ක නල මාර්ගයක්

ඉහත ප්‍රශ්නයට ආපසු යමින් පහත සඳහන් උපදෙස් සලකා බලමු:

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

ශාඛා අනාවැකි නොමැතිව, පහත සඳහන් දේ සිදුවනු ඇත:

උපදෙස් B හෝ උපදෙස් ක්‍රියාත්මක කිරීම සඳහා ප්‍රොසෙසරයට නල මාර්ගයේ EX අදියර තෙක් උපදෙස් ලබා නොගන්නා තෙක් බලා සිටීමට සිදුවනු ඇත, මන්ද B හෝ උපදෙස් C වෙත යාමට තීරණය කිරීම උපදෙස්වල ප්‍රති result ලය මත රඳා පවතී. එබැවින් නල මාර්ගය මේ වගේ වෙයි.

කොන්දේසිය සත්‍ය වූ විට: රූප විස්තරය මෙහි ඇතුළත් කරන්න

කොන්දේසිය වැරදියි නම්: රූප විස්තරය මෙහි ඇතුළත් කරන්න

උපදෙස් A හි ප්‍රති result ලය බලාපොරොත්තුවෙන් සිටීමේ ප්‍රති result ලයක් ලෙස, ඉහත නඩුවේ (ශාඛා අනාවැකි නොමැතිව; සත්‍ය සහ අසත්‍ය යන දෙකටම) වැය කළ මුළු CPU චක්‍ර 7 කි.

ශාඛා පුරෝකථනය යනු කුමක්ද?

ශාඛා අනාවැකි කරුවෙකු මෙය නිශ්චිතව දැන ගැනීමට පෙර ශාඛාවක් (එසේ නම්-එසේ නම් වෙනත් ව්‍යුහයක්) යන්නේ කුමන දිශාවටදැයි අනුමාන කිරීමට උත්සාහ කරයි. A උපදෙස් නල මාර්ගයේ EX අදියර කරා ළඟා වන තෙක් බලා නොසිටිනු ඇත, නමුත් එය තීරණය අනුමාන කර එම උපදෙස් වෙත යනු ඇත (අපගේ උදාහරණය සම්බන්ධයෙන් B හෝ C).

නිවැරදි අනුමානයක දී, නල මාර්ගය මේ වගේ දෙයක් පෙනේ: රූප විස්තරය මෙහි ඇතුළත් කරන්න

අනුමානය වැරදියි කියා පසුව අනාවරණය වුවහොත්, අර්ධ වශයෙන් ක්‍රියාත්මක කරන ලද උපදෙස් ඉවතලන අතර නල මාර්ගය නිවැරදි ශාඛාව සමඟ ආරම්භ වන අතර එය ප්‍රමාදයකි. ශාඛා වැරදි පුරෝකථනයකදී නාස්ති වන කාලය නල මාර්ගයේ සිට අදියර දක්වා ක්‍රියාත්මක කිරීමේ අදියර දක්වා සමාන වේ. නූතන මයික්‍රොප්‍රොසෙසරවල තරමක් දිගු නල මාර්ග ඇති බැවින් වැරදි අනාවැකි ප්‍රමාදය ඔරලෝසු චක්‍ර 10 ත් 20 ත් අතර වේ. නල මාර්ගය දිගු වන විට හොඳ ශාඛා අනාවැකි කරුවෙකුගේ අවශ්‍යතාවය වැඩි වේ.

OP කේතයේ, පළමු වරට කොන්දේසි සහිත, ශාඛා අනාවැකි කරුවන්ට අනාවැකි පදනම් කර ගැනීමට කිසිදු තොරතුරක් නොමැති බැවින් පළමු වරට එය ඊළඟ උපදෙස් අහඹු ලෙස තෝරා ගනු ඇත. පසුකාලීනව for loop හි, එය ඉතිහාසය මත පුරෝකථනය පදනම් කර ගත හැකිය. ආරෝහණ අනුපිළිවෙලට වර්ග කර ඇති විට, හැකියාවන් තුනක් ඇත:

  1. සියලුම මූලද්රව්ය 128 ට වඩා අඩුය
  2. සියලුම මූලද්රව්ය 128 ට වඩා වැඩිය
  3. සමහර ආරම්භක නව මූලද්‍රව්‍ය 128 ට වඩා අඩු වන අතර පසුව එය 128 ට වඩා විශාල වේ

අනාවැකි පළමුවෙන්ම පළමු ශාඛාවේ සත්‍ය ශාඛාව උපකල්පනය කරනු ඇතැයි අපි උපකල්පනය කරමු.

පළමු අවස්ථාවේ දී, always තිහාසිකව එහි සියලු අනාවැකි නිවැරදි බැවින් එය සැමවිටම සත්‍ය ශාඛාව ගනී. 2 වන අවස්ථාවෙහිදී, මුලදී එය වැරදි ලෙස පුරෝකථනය කරනු ඇත, නමුත් නැවත නැවත කිහිපයකින් පසුව, එය නිවැරදිව පුරෝකථනය කරනු ඇත. 3 වන අවස්ථාවෙහිදී, මූලද්‍රව්‍ය 128 ට වඩා අඩු වන තෙක් එය මුලින් නිවැරදිව පුරෝකථනය කරනු ඇත. ඉන්පසු එය යම් කාලයක් සඳහා අසමත් වන අතර ඉතිහාසයේ ශාඛා පුරෝකථන අසාර්ථකත්වය දකින විට එය නිවැරදි වේ.

මෙම සියලු අවස්ථාවන්හිදී අසමත් වීම සංඛ්‍යාවට වඩා අඩු වනු ඇති අතර එහි ප්‍රති as ලයක් වශයෙන්, අර්ධ වශයෙන් ක්‍රියාත්මක කරන ලද උපදෙස් ඉවත දමා නිවැරදි ශාඛාව සමඟ ආරම්භ කිරීමට අවශ්‍ය වන්නේ කිහිප වතාවක් පමණි, එහි ප්‍රති CP ලයක් ලෙස CPU චක්‍ර අඩු වේ.

නමුත් අහඹු ලෙස වර්ගීකරණය නොකළ අරාවක දී, අනාවැකිය සඳහා අර්ධ වශයෙන් ක්‍රියාත්මක කරන ලද උපදෙස් ඉවත දමා නිවැරදි ශාඛාව සමඟ බොහෝ විට ආරම්භ කළ යුතු අතර, වර්ග කළ අරාව හා සසඳන විට වැඩි CPU චක්‍රයක් ඇති වේ.


1
උපදෙස් දෙකක් එකට ක්‍රියාත්මක වන්නේ කෙසේද? මෙය වෙනම cpu මධ්‍යයකින් සිදු කර තිබේද? නැතහොත් නල මාර්ග උපදෙස් තනි cpu හරය සමඟ ඒකාබද්ධ වී තිබේද?
එම්.කසෙම් අක්ගරි

1
@ M.kazemAkhgary මේ සියල්ල එක තාර්කික හරයක් තුළ ඇත. ඔබ කැමති නම්, මෙය නිදසුනක් ලෙස ඉන්ටෙල් මෘදුකාංග සංවර්ධක අත්පොතෙහි
Sergey.quixoticaxis.Ivanov

728

නිල පිළිතුරක් ලැබෙන්නේ

  1. ඉන්ටෙල් - ශාඛා වැරදි අනාවැකි වලක්වා ගැනීම
  2. ඉන්ටෙල් - වැරදි අනාවැකි වැළැක්වීම සඳහා ශාඛාව සහ ලූප ප්‍රතිසංවිධානය
  3. විද්‍යාත්මක පත්‍රිකා - ශාඛා පුරෝකථන පරිගණක ගෘහ නිර්මාණ ශිල්පය
  4. පොත්: ජේ. එල්. හෙනසි, ඩී. පැටසන්: පරිගණක ගෘහ නිර්මාණ ශිල්පය: ප්‍රමාණාත්මක ප්‍රවේශයකි
  5. විද්‍යාත්මක ප්‍රකාශනවල ලිපි: ටී. වයි, වයි. එන්. පැට් ශාඛා අනාවැකි මත මේවායින් බොහොමයක් ඉදිරිපත් කළේය.

ශාඛා අනාවැකි කරුවෙකු ව්‍යාකූල වන්නේ මන්දැයි ඔබට මෙම සුන්දර රූප සටහනෙන් දැක ගත හැකිය .

ද්වි-බිට් රාජ්‍ය රූප සටහන

මුල් කේතයේ සෑම අංගයක්ම අහඹු අගයකි

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

ඒ නිසා අනාවැකිය std::rand()පහරක් ලෙස පැති වෙනස් කරයි .

අනෙක් අතට, එය වර්ග කළ පසු, අනාවැකි පළමුවෙන්ම දැඩි ලෙස නොගන්නා තත්වයකට ගමන් කරනු ඇති අතර, අගයන් ඉහළ අගයට වෙනස් වූ විට, අනාවැකි කරුවෙකු ලකුණු තුනකින් වෙනස් වනු ඇත.



697

එකම පේළියේම (මෙය කිසිදු පිළිතුරකින් ඉස්මතු කර නොතිබූ බව මම සිතමි) සමහර විට (විශේෂයෙන් ලිනක්ස් කර්නලයේ මෙන් කාර්ය සාධනය වැදගත් වන මෘදුකාංගවල) පහත සඳහන් ප්‍රකාශ වැනි ප්‍රකාශ ඔබට සොයා ගත හැකිය:

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

හෝ ඒ හා සමානව:

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

දෙකම likely()සහ unlikely()ඇත්ත වශයෙන්ම මැක්‍රෝස් යනු ජීසීසී වැනි දෙයක් භාවිතා කිරීමෙන් __builtin_expectපරිශීලකයා විසින් සපයනු ලබන තොරතුරු සැලකිල්ලට ගනිමින් කොන්දේසියට අනුග්‍රහය දැක්වීම සඳහා අනාවැකි කේතය ඇතුළු කිරීමට සම්පාදකයාට උපකාර කිරීමයි. ක්‍රියාත්මක වන වැඩසටහනේ හැසිරීම වෙනස් කිරීමට හෝ හැඹිලිය ඉවත් කිරීම වැනි පහත් මට්ටමේ උපදෙස් විමෝචනය කළ හැකි වෙනත් බිල්ඩින් සඳහා GCC සහාය දක්වයි. මෙම ලේඛනය බලන්න . ලබා ගත හැකි GCC හි බිල්ඩින් හරහා යන .

සාමාන්‍යයෙන් මේ ආකාරයේ ප්‍රශස්තිකරණයන් ප්‍රධාන වශයෙන් දක්නට ලැබෙන්නේ දෘ-තථ්‍ය කාලීන යෙදුම්වල හෝ ක්‍රියාත්මක කිරීමේ වේලාව වැදගත් වන කාවැද්දූ පද්ධතිවල ය. උදාහරණයක් ලෙස, ඔබ 1/10000000 වාරයක් පමණක් සිදුවන යම් දෝෂයක් තිබේදැයි පරීක්ෂා කරන්නේ නම්, මේ පිළිබඳව සම්පාදකයාට දැනුම් නොදෙන්නේ ඇයි? මේ ආකාරයෙන්, පෙරනිමියෙන්, ශාඛා අනාවැකිය උපකල්පනය කරනුයේ කොන්දේසිය අසත්‍ය බවය.


679

සී ++ හි නිතර භාවිතා වන බූලියන් මෙහෙයුම් සම්පාදනය කරන ලද වැඩසටහනේ බොහෝ ශාඛා නිපදවයි. මෙම ශාඛා ලූප තුළ තිබේ නම් සහ ඒවා ක්‍රියාත්මක කිරීම සැලකිය යුතු ලෙස මන්දගාමී කළ හැකි යැයි අනාවැකි කීමට අපහසු නම්. බූලීය විචල්ය අගය සමග 8-bit සංඛ්යාවක් ලෙස ගබඩා කර 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;
}

මෙම කේතය ප්‍රශස්ත නොවේ. වැරදි අනාවැකි සම්බන්ධයෙන් ශාඛා දිගු කාලයක් ගතවනු ඇත. එය operands වෙනත් කිසිදු වටිනාකම් වඩා ඇති බව නිශ්චිතව දැන නම්, බූලියන් මෙහෙයුම් වඩා කාර්යක්ෂම කළ හැකි 0හා 1. සම්පාදකයා එවැනි උපකල්පනයක් නොකිරීමට හේතුව, විචල්‍යයන් ආරම්භ නොකළ හෝ නොදන්නා ප්‍රභවයන්ගෙන් පැමිණිය හොත් වෙනත් අගයන් තිබිය හැකිය. ඉහත කේතය නම් වැඩිදියුණු කළ හැකි aහා bඔවුන් බූලීය ප්රතිදානය නිෂ්පාදනය කරන සමාගම් ආවේ නම් වලංගු අගයන් ආරම්භනය කර ඇත හෝ. ප්‍රශස්තිකරණය කළ කේතය මේ වගේ ය:

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

charboolබිට්වේස් ක්‍රියාකරුවන් භාවිතා කිරීමට හැකිවන පරිදි ඒ වෙනුවට භාවිතා කරයි (& සහ |) ඒ වෙනුවට බූලියන් ඔපරේටර් (ක &&හා ||). බිට්වේස් ක්‍රියාකරුවන් යනු එක් ඔරලෝසු චක්‍රයක් පමණක් ගන්නා තනි උපදෙස් වේ. මෙම හෝ ක්රියාකරු ( |පවා නම්) වැඩ aසහ bහැර වෙනත් සාරධර්ම 0හෝ 1. මෙම සහ ක්රියාකරු ( &) සහ ෙවන ෙවනම සහ ලකුණින් බැහැරව හෝ ක්රියාකරු ( ^) නොගැලපෙන ප්රතිඵල operands වෙනත් වටිනාකම් වඩා තිබේ නම් ලබා දෙන්න 0සහ 1.

~NOT සඳහා භාවිතා කළ නොහැක. ඒ වෙනුවට, ඔබට දන්නා විචල්‍යයක 0හෝ 1XOR'ing මගින් බූලියන් NOT එකක් සෑදිය හැකිය 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;

බොහෝ අවස්ථාවන්හීදී ප්‍රශස්ත වේ ( &&ප්‍රකාශනය බොහෝ ශාඛා වැරදි අනාවැකි ජනනය කරනු ඇතැයි ඔබ අපේක්ෂා නොකරන්නේ නම් ).


342

ඒක ස්ථිරයි! ...

ඔබේ කේතයේ සිදුවන මාරුව නිසා ශාඛා පුරෝකථනය මඟින් තර්කනය මන්දගාමී වේ. එය හරියට ඔබ කෙළින් වීථියකට හෝ බොහෝ හැරීම් සහිත වීථියකට යනවා හා සමානයි, නිසැකවම කෙළින්ම එක ඉක්මනින් සිදු වේවි! ...

අරාව වර්ග කර ඇත්නම්, පළමු පියවරේදී ඔබගේ තත්වය සාවද්‍ය වේ: data[c] >= 128 , එවිට වීථියේ අවසානය දක්වා වූ මුළු මාර්ගය සඳහාම සත්‍ය වටිනාකමක් බවට පත්වේ. ඔබ තර්කනයේ අවසානයට වේගයෙන් ළඟා වන්නේ එලෙසිනි. අනෙක් අතට, වර්ගීකරණය නොකළ අරාව භාවිතා කරමින්, ඔබට බොහෝ හැරීම් සහ සැකසුම් අවශ්‍ය වන අතර එමඟින් ඔබේ කේතය මන්දගාමීව ක්‍රියාත්මක වේ ...

මම ඔබ වෙනුවෙන් නිර්මාණය කළ රූපය පහතින් බලන්න. වේගයෙන් නිම කිරීමට යන්නේ කුමන වීදියද?

ශාඛා පුරෝකථනය

එබැවින් වැඩසටහන්මය වශයෙන් ශාඛා පුරෝකථනය මඟින් ක්‍රියාවලිය මන්දගාමී වේ ...

අවසානයේදී, ඔබේ කේතයට වෙනස් ආකාරයකින් බලපානු ඇති බවට ශාඛා අනාවැකි වර්ග දෙකක් අප සතුව ඇති බව දැන ගැනීම හොඳය:

1. ස්ථිතික

2. ගතික

ශාඛා පුරෝකථනය

ස්ථිතික ශාඛා පුරෝකථනය පළමු වරට කොන්දේසි සහිත ශාඛාවක් හමු වූ විට මයික්‍රොප්‍රොසෙසරය භාවිතා කරන අතර කොන්දේසි සහිත ශාඛා කේතය සාර්ථකව ක්‍රියාත්මක කිරීම සඳහා ගතික ශාඛා පුරෝකථනය භාවිතා කරයි.

මෙම නීති වාසිය ලබා ගැනීම සඳහා ඵලදායී ඔබගේ කේතය ලිවීම සඳහා, ලියන විට නම්-වෙන හෝ මාරු ප්රකාශ, පළමු හා ක්රම ක්රමයෙන් අඩු අවම වශයෙන් පොදු වැඩ කටයුතු බොහෝ අවස්ථාවන්හීදී පරීක්ෂා කරන්න. සාමාන්‍යයෙන් භාවිතා කරනුයේ ලූප් ඉරේටරයේ තත්වය පමණක් බැවින් ස්ථිතික ශාඛා පුරෝකථනය සඳහා ලූපවලට විශේෂ කේත අනුපිළිවෙලක් අවශ්‍ය නොවේ.


304

මෙම ප්‍රශ්නයට දැනටමත් බොහෝ වාර ගණනක් විශිෂ්ට පිළිතුරු ලබා දී ඇත. තවමත් මම තවත් සිත්ගන්නාසුලු විශ්ලේෂණයකට කණ්ඩායමේ අවධානය යොමු කිරීමට කැමතියි.

වින්ඩෝස් හි වැඩසටහන තුළම කේත කැබැල්ලක් පැතිකඩ කළ හැකි ආකාරය නිරූපණය කිරීමේ ක්‍රමයක් ලෙස මෑතකදී මෙම උදාහරණය (ඉතා සුළු වශයෙන් වෙනස් කරන ලදි) භාවිතා කරන ලදී. ඒ අතරම, කතුවරයා විසින් කේතය වැඩි කාලයක් ගත කරන්නේ කොතැනද යන්න තීරණය කිරීම සඳහා ප්‍රති results ල භාවිතා කරන්නේ කෙසේදැයි පෙන්වයි. අවසාන වශයෙන්, එච්.ඒ.එල් (දෘඩාංග වියුක්ත කිරීමේ ස්ථරයේ) හි සුළු දන්නා අංගයක් භාවිතා කරන්නේ කෙසේද යන්නත්, වර්ගීකරණය නොකළ නඩුවේ ශාඛා වැරදි අනාවැකි කොපමණ ප්‍රමාණයක් සිදුවන්නේද යන්න තීරණය කරයි.

සබැඳිය මෙහි ඇත: http://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/profile/demo.htm


3
එය ඉතා රසවත් ලිපියකි (ඇත්ත වශයෙන්ම, මම ඒ සියල්ල කියවා ඇත්තෙමි), නමුත් එය ප්‍රශ්නයට පිළිතුරු දෙන්නේ කෙසේද?
පීටර් මෝර්ටෙන්සන්

2
EtPeterMortensen මම ඔබේ ප්‍රශ්නයෙන් ටිකක් අමනාප වී සිටිමි. උදාහරණයක් ලෙස එම කැබැල්ලෙන් අදාළ එක් පේළියක් මෙහි ඇත: 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. කතුවරයා මෙහි පළ කර ඇති කේතයේ සන්දර්භය තුළ පැතිකඩ සාකච්ඡා කිරීමට උත්සාහ කරන අතර ක්‍රියාවලියේදී වර්ග කළ නඩුව මෙතරම් වේගවත් වන්නේ මන්දැයි පැහැදිලි කිරීමට උත්සාහ කරයි.
සදහටම ඉගෙනීම

261

දැනටමත් අන් අය සඳහන් කර ඇති පරිදි, අභිරහස පිටුපස ඇත්තේ ශාඛා පුරෝකථනය කරන්නා ය .

මම යමක් එකතු කිරීමට උත්සාහ නොකර සංකල්පය වෙනත් ආකාරයකින් පැහැදිලි කරමි. විකියෙහි සංක්ෂිප්ත හැඳින්වීමක් ඇත, එහි පෙළ සහ රූප සටහන අඩංගු වේ. ශාඛා පුරෝකථනය බුද්ධිමත්ව විස්තාරණය කිරීම සඳහා රූප සටහනක් භාවිතා කරන පහත පැහැදිලි කිරීමට මම කැමතියි.

පරිගණක ගෘහ නිර්මාණ ශිල්පයෙහි, ශාඛා අනාවැකි යනු ඩිජිටල් පරිපථයකි, මෙය ස්ථිරවම දැන ගැනීමට පෙර ශාඛාවක් (උදා: එසේ නම්-එසේ නම් වෙනත් ව්‍යුහයක්) යන්නේ කුමන දිශාවටදැයි අනුමාන කිරීමට උත්සාහ කරයි. ශාඛා අනාවැකි කරන්නාගේ අරමුණ වන්නේ උපදෙස් නල මාර්ගයේ ගලායාම වැඩි දියුණු කිරීමයි. X86 වැනි නවීන නල මාර්ගගත මයික්‍රොප්‍රොසෙසර් ගෘහ නිර්මාණ ශිල්පයෙහි ඉහළ performance ලදායී කාර්ය සාධනයක් ලබා ගැනීම සඳහා ශාඛා පුරෝකථනයන් තීරණාත්මක කාර්යභාරයක් ඉටු කරයි.

ද්වි-මාර්ග අතු බෙදීම සාමාන්‍යයෙන් කොන්දේසි සහිත පැනීමේ උපදෙස් සමඟ ක්‍රියාත්මක වේ. කොන්දේසි සහිත පැනීම "ගත නොහැකි" විය හැකි අතර කොන්දේසිගත පැනීමෙන් පසු වහාම එන කේතයේ පළමු ශාඛාව සමඟ දිගටම ක්‍රියාත්මක කළ හැකිය, නැතහොත් එය "ගෙන" ගත හැකි අතර කේතයේ දෙවන ශාඛාව පවතින වැඩසටහන් මතකයේ වෙනත් ස්ථානයකට පනින්න. ගබඩා කර ඇත. කොන්දේසිය ගණනය කර කොන්දේසි සහිත පැනීම උපදෙස් නල මාර්ගයේ ක්‍රියාත්මක කිරීමේ අදියර පසු කරන තුරු කොන්දේසි සහිත පැනීමක් සිදු වේද යන්න නිශ්චිතවම නොදනී (රූපය 1 බලන්න).

රූපය 1

විස්තර කර ඇති තත්වය මත පදනම්ව, විවිධ අවස්ථා වලදී නල මාර්ගයක උපදෙස් ක්‍රියාත්මක කරන්නේ කෙසේදැයි පෙන්වීමට මම සජීවිකරණ නිරූපණයක් ලියා ඇත.

  1. ශාඛා පුරෝකථනය කරන්නා නොමැතිව.

ශාඛා අනාවැකි නොමැතිව, ප්‍රොසෙසරයට කොන්දේසි සහිත පැනීමේ උපදෙස් ක්‍රියාත්මක වන අදියර පසු කරන තෙක් බලා සිටීමට සිදුවනු ඇත.

උදාහරණයේ උපදෙස් තුනක් අඩංගු වන අතර පළමුවැන්න කොන්දේසි සහිත පැනීමේ උපදෙස් වේ. කොන්දේසි සහිත පැනීමේ උපදෙස් ක්‍රියාත්මක වන තෙක් අවසාන උපදෙස් දෙක නල මාර්ගයට යා හැකිය.

ශාඛා අනාවැකි නොමැතිව

උපදෙස් 3 ක් සම්පූර්ණ කිරීම සඳහා ඔරලෝසු චක්‍ර 9 ක් ගත වේ.

  1. ශාඛා පුරෝකථනය භාවිතා කරන්න සහ කොන්දේසි සහිත පැනීමක් නොකරන්න. අනාවැකිය කොන්දේසි සහිත පැනීමක් නොවේ යැයි උපකල්පනය කරමු .

රූප විස්තරය මෙහි ඇතුළත් කරන්න

උපදෙස් 3 ක් සම්පූර්ණ කිරීම සඳහා ඔරලෝසු චක්‍ර 7 ක් ගත වේ.

  1. ශාඛා පුරෝකථනය භාවිතා කර කොන්දේසි සහිත පැනීමක් කරන්න. අනාවැකිය කොන්දේසි සහිත පැනීමක් නොවේ යැයි උපකල්පනය කරමු .

රූප විස්තරය මෙහි ඇතුළත් කරන්න

උපදෙස් 3 ක් සම්පූර්ණ කිරීම සඳහා ඔරලෝසු චක්‍ර 9 ක් ගත වේ.

ශාඛා වැරදි පුරෝකථනයකදී නාස්ති වන කාලය නල මාර්ගයේ සිට අදියර දක්වා ක්‍රියාත්මක කිරීමේ අදියර දක්වා සමාන වේ. නූතන මයික්‍රොප්‍රොසෙසරවල තරමක් දිගු නල මාර්ග ඇති බැවින් වැරදි අනාවැකි ප්‍රමාදය ඔරලෝසු චක්‍ර 10 ත් 20 ත් අතර වේ. එහි ප්‍රති As ලයක් ලෙස නල මාර්ගයක් දිගු කිරීම වඩාත් දියුණු ශාඛා අනාවැකි කරුවෙකුගේ අවශ්‍යතාවය වැඩි කරයි.

ඔබට පෙනෙන පරිදි, ශාඛා පුරෝකථනය භාවිතා නොකිරීමට අපට හේතුවක් නොමැති බව පෙනේ.

ශාඛා පුරෝකථනය කරන්නාගේ මූලික කොටස පැහැදිලි කරන ඉතා සරල නිරූපණයකි. එම gifs කරදරකාරී නම්, කරුණාකර ඒවා පිළිතුරෙන් ඉවත් කිරීමට නිදහස්ව සිටින්න. තවද අමුත්තන්ට සජීවී නිරූපණ ප්‍රභව කේතය BranchPredictorDemo වෙතින් ලබා ගත හැකිය.


1
ඉන්ටෙල් අලෙවිකරණ සජීවිකරණ තරම්ම පාහේ හොඳ ඒවා වන අතර ඒවා ශාඛා අනාවැකි ගැන පමණක් නොව පිළිවෙලට ක්‍රියාත්මක නොකිරීම නිසා උපාය මාර්ග දෙකම "සමපේක්ෂන" වේ. මතකයේ සහ ගබඩාවේ ඉදිරියට කියවීම (බෆරයට අනුක්‍රමික පෙර-ලබා ගැනීම) ද සමපේක්ෂනාත්මක ය. ඒ සියල්ල එකතු වේ.
mckenzm

cmckenzm: යල්පැනගිය සමපේක්ෂන ක්‍රියාත්මක කිරීම ශාඛා අනාවැකි වඩාත් වටිනාකමක් ඇති කරයි; ලබා ගැනීමේ / විකේතනය කිරීමේ බුබුලු සැඟවීම මෙන්ම, ශාඛා පුරෝකථනය + සමපේක්ෂන ක්‍රියාත්මක කිරීම තීරණාත්මක මාර්ග ප්‍රමාදයෙන් පාලන පරායත්තයන් ඉවත් කරයි. ශාඛා තත්ත්වය දැන ගැනීමට පෙරif() බ්ලොක් එකක් තුළ හෝ පසුව කේතය ක්‍රියාත්මක කළ හැකිය . හෝ වැනි සෙවුම් පුඩුවක් සඳහා හෝ , interations පැටලෙන්නේ හැක. මීළඟ ක්‍රියාවලිය නැවත ක්‍රියාත්මක කිරීමට පෙර තරගය හෝ ප්‍රති result ලය දැන ගැනීමට ඔබට බලා සිටීමට සිදුවුවහොත්, ප්‍රතිදානය වෙනුවට හැඹිලි භාරය + ALU ප්‍රමාදය ඔබට බාධාවක් වනු ඇත. strlenmemchr
පීටර් කෝර්ඩ්ස්

210

ශාඛා-පුරෝකථන වාසි!

ශාඛා වැරදි අනාවැකි මඟින් වැඩසටහන් මන්දගාමී නොවන බව වටහා ගැනීම වැදගත්ය. මඟ හැරුණු පුරෝකථනයක පිරිවැය හරියට ශාඛා අනාවැකි නොපවතින අතර, කුමන කේතය ක්‍රියාත්මක කළ යුතුද යන්න තීරණය කිරීම සඳහා ඔබ ප්‍රකාශනය ඇගයීමට ලක් කරන තෙක් බලා සිටියේය (ඊළඟ ඡේදයේ වැඩිදුර පැහැදිලි කිරීම).

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

if-else\ switchප්‍රකාශයක් ඇති සෑම අවස්ථාවකම , කුමන කොටස ක්‍රියාත්මක කළ යුතුද යන්න තීරණය කිරීම සඳහා ප්‍රකාශනය ඇගයීමට ලක් කළ යුතුය. සම්පාදකයා විසින් ජනනය කරන ලද එකලස් කිරීමේ කේතයේ, කොන්දේසි සහිත ශාඛා උපදෙස් ඇතුළත් කරනු ලැබේ.

ශාඛා උපදෙස් මඟින් පරිගණකයක් වෙනත් උපදෙස් අනුක්‍රමයක් ක්‍රියාත්මක කිරීමට පටන් ගත හැකි අතර එමඟින් උපදෙස් ක්‍රියාත්මක කිරීමේ පෙරනිමි හැසිරීම් වලින් බැහැර විය හැකිය (එනම් ප්‍රකාශනය අසත්‍ය නම්, වැඩසටහන ifවාරණ කේතය මඟ හැරේ ) යම් කොන්දේසියක් මත පදනම්ව, අපගේ නඩුවේ ප්‍රකාශන ඇගයීම.

එසේ පැවසුවහොත්, සම්පාදකයා එය සැබවින්ම ඇගයීමට පෙර ප්‍රති come ලය පුරෝකථනය කිරීමට උත්සාහ කරයි. එය වාරණයෙන් උපදෙස් ලබා ගන්නා ifඅතර ප්‍රකාශනය සත්‍ය බවට පත්වුවහොත් පුදුම සහගතය! අපි එය ඇගයීමට ගතවන කාලය ලබාගෙන කේතයේ ප්‍රගතියක් ලබා ගත්තෙමු. එසේ නොවේ නම් අපි වැරදි කේතය ධාවනය කරන්නෙමු, නල මාර්ගය ගලවා, නිවැරදි වාරණය ක්‍රියාත්මක වේ.

දෘශ්‍යකරණය:

ඔබට මාර්ගය 1 හෝ මාර්ගය 2 තෝරා ගැනීමට අවශ්‍ය යැයි කියමු. ඔබේ සහකරු සිතියම පරික්ෂා කරන තෙක් බලා සිටීම, ඔබ ## හි නතර වී බලා සිට ඇත, නැතහොත් ඔබට මාර්ග 1 තෝරා ගත හැකි අතර ඔබ වාසනාවන්ත නම් (මාර්ගය 1 නිවැරදි මාර්ගයයි), එවිට ඔබගේ සහකරු සිතියම පරික්ෂා කරන තෙක් ඔබ බලා සිටිය යුතු නැත (සිතියම පරික්ෂා කිරීමට ඔහුට ගතවන කාලය ඔබ ඉතිරි කළේය), එසේ නොමැතිනම් ඔබ ආපසු හැරී යනු ඇත.

නල මාර්ග නළා ගැනීම වේගවත් වන අතර වර්තමානයේ මෙම සූදුව ගැනීම වටී. වර්ග කළ දත්ත හෝ සෙමින් වෙනස් වන දත්ත පුරෝකථනය කිරීම සෑම විටම වේගවත් වෙනස්කම් පුරෝකථනය කිරීමට වඩා පහසු සහ වඩා හොඳය.

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

නල මාර්ග නළා ගැනීම වේගවත් වන අතර ඇත්ත වශයෙන්ම නොවේ. හැඹිලියක් සමඟ සසඳන විට එය වේගවත් වේ, නමුත් නවීන ඉහළ කාර්යසාධනයක් සහිත x86 (ඉන්ටෙල් සැන්ඩිබ්‍රිජ්-පවුල වැනි) මත එය චක්‍ර දුසිමක් පමණ වේ. යථා තත්ත්වයට පත්වීමට පෙර සියලු පැරණි ස්වාධීන උපදෙස් විශ්‍රාම ගැන්වීම සඳහා බලා සිටීම වළක්වා ගැනීමට වේගවත් ප්‍රකෘතිමත් වීමක් ඉඩ දී ඇතත්, වැරදි අනාවැකියක් මත ඔබට තවමත් ඉදිරිපස චක්‍ර විශාල ප්‍රමාණයක් අහිමි වේ. ස්කයිලේක් සීපීයූ ශාඛාවක් වැරදි ලෙස පුරෝකථනය කළ විට හරියටම කුමක් සිදුවේද? . (තවද සෑම චක්‍රයක්ම වැඩ උපදෙස් 4 ක් පමණ විය හැකිය.) ඉහළ ප්‍රතිදාන කේත සඳහා නරක ය.
පීටර් කෝර්ඩ්ස්

153

ARM හි, කිසිදු ශාඛාවක් අවශ්‍ය නොවේ, මන්ද සෑම උපදේශනයකටම බිටු 4 ක කොන්දේසි සහිත ක්ෂේත්‍රයක් ඇති අතර, එය ප්‍රොසෙසර් තත්ව ලේඛනයේ ඇති විය හැකි විවිධ කොන්දේසි 16 න් ඕනෑම එකක් (ශුන්‍ය පිරිවැයකින්) පරීක්ෂා කරයි , සහ උපදෙස් මත කොන්දේසිය තිබේ නම් අසත්ය, උපදෙස් මඟ හැරී ඇත. මෙය කෙටි ශාඛා වල අවශ්‍යතාවය ඉවත් කරන අතර මෙම ඇල්ගොරිතම සඳහා ශාඛා පුරෝකථනය කිරීමක් සිදු නොවේ. එමනිසා, මෙම ඇල්ගොරිතමයේ වර්ග කළ අනුවාදය ARM හි වර්ගීකරණය නොකළ අනුවාදයට වඩා මන්දගාමී වනු ඇත.

මෙම ඇල්ගොරිතම සඳහා වන අභ්‍යන්තර ලූපය ARM එකලස් කිරීමේ භාෂාවෙන් පහත දැක්වෙන ආකාරයට පෙනේ:

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උපදෙස් සඳහා විකල්ප උපසර්ගයක් එක් කළහොත් මිස වෙනත් බොහෝ උපදෙස් PSR ස්පර්ශ නොකරනු ඇත. උපදෙස්වල ප්‍රති result ලය. හුදෙක් 4-බිට් තත්ත්වය ෙපර ෙයදුම මෙන් PSR බලපාන තොරව උපදෙස් ක්රියාත්මක කිරීමට හැකි වීම ARM ශාඛා අවශ්යතාව අඩු, සහ ද දෘඩාංග මට්ටමේ නියෝගයක් සරයක පිටතට සඳහා පහසුකම් සැලසීමට යාන්තණය නිසා යාවත්කාලීන බව සමහර මෙහෙයුම් X සිදු පසු, තත්ව බිටු, පසුව (හෝ සමාන්තරව) ඔබට තත්ව බිටු වලට පැහැදිලිවම බලපාන්නේ නැති වෙනත් කාර්යයන් රාශියක් කළ හැකිය, එවිට ඔබට කලින් X විසින් නියම කරන ලද තත්ව බිටු වල තත්වය පරීක්ෂා කළ හැකිය.

තත්ව පරීක්ෂණ ක්ෂේත්‍රය සහ විකල්ප “සැකසුම් තත්ව බිට්” ක්ෂේත්‍රය ඒකාබද්ධ කළ හැකිය, උදාහරණයක් ලෙස:

  • ADD R1, R2, R3R1 = R2 + R3කිසිදු තත්ව බිටු යාවත්කාලීන නොකර ක්‍රියා කරයි .
  • ADDGE R1, R2, R3 එකම මෙහෙයුම සිදු කරනුයේ තත්ව බිටු වලට බලපාන පෙර උපදෙස් මගින් වඩා විශාල හෝ සමාන තත්වයකට පත්වුවහොත් පමණි.
  • ADDS R1, R2, R3කාර්ය ඉටු එකතු පසුව යාවත්කාලීන N, Z, Cහා Vප්රතිඵලය ඍණ ද යන්න මත පදනම් වූ Processor තත්වය රෙජිස්ටර් කොඩි, ශුන්ය, (නිලකුණු අමතරව සඳහා) ස්ථානමාරුව, හෝ නාන්නට (අත්සන් අමතරව සඳහා).
  • ADDSGE R1, R2, R3එකතු කිරීම සිදු කරන්නේ GEපරීක්ෂණය සත්‍ය නම් පමණක් වන අතර පසුව එකතු කිරීමේ ප්‍රති result ලය මත පදනම්ව තත්ව බිටු යාවත්කාලීන කරයි.

ලබා දී ඇති මෙහෙයුමක් සඳහා තත්ව බිටු යාවත්කාලීන කළ යුතුද නැද්ද යන්න සඳහන් කිරීමට බොහෝ ප්‍රොසෙසර් ගෘහ නිර්මාණ ශිල්පයට මෙම හැකියාව නොමැත, එමඟින් තත්ව බිටු සුරැකීමට හා පසුව යථා තත්වයට පත් කිරීමට අමතර කේත ලිවීමට අවශ්‍ය විය හැකිය, නැතහොත් අමතර ශාඛා අවශ්‍ය විය හැකිය, නැතහොත් සකසනය ඉවත් කිරීම සීමා කළ හැකිය. ඇණවුම ක්‍රියාත්මක කිරීමේ කාර්යක්ෂමතාව: බොහෝ CPU උපදෙස් කට්ටල ගෘහ නිර්මාණ ශිල්පයේ අතුරු ආබාධවලින් එකක් වන්නේ බොහෝ උපදෙස් වලින් පසුව තත්ව බිටු බලහත්කාරයෙන් යාවත්කාලීන කිරීමයි, එකිනෙකාට මැදිහත් නොවී සමාන්තරව ක්‍රියාත්මක කළ හැකි උපදෙස් විහිළුවට ලක් කිරීම වඩා දුෂ්කර ය. තත්ව බිටු යාවත්කාලීන කිරීම අතුරු ආබාධ ඇති කරයි, එබැවින් කේතයට රේඛීයකරණ බලපෑමක් ඇත.එකලස් කිරීමේ භාෂා ක්‍රමලේඛකයින් සහ සම්පාදකයින් යන දෙකටම අතිශයින්ම බලවත් වන අතර ඉතා කාර්යක්ෂම කේතයක් නිපදවන ඕනෑම උපදෙස් වලින් පසු තත්ව බිටු යාවත්කාලීන කිරීමට හෝ යාවත්කාලීන කිරීමට විකල්පයක් ඇති ඕනෑම උපදෙස් මත ශාඛා රහිත තත්ව පරීක්ෂාව මිශ්‍ර කිරීමට හා ගැලපීමට ARM සතු හැකියාව.

ARM මෙතරම් විශිෂ්ට ලෙස සාර්ථක වූයේ මන්දැයි ඔබ කවදා හෝ කල්පනා කර ඇත්නම්, මෙම යාන්ත්‍රණ දෙකෙහි දීප්තිමත් effectiveness ලදායීතාවය සහ අන්තර් ක්‍රියාකාරිත්වය කතාවේ විශාල කොටසක් වේ, මන්ද ඒවා ARM ගෘහ නිර්මාණ ශිල්පයේ කාර්යක්ෂමතාවයේ විශිෂ්ටතම ප්‍රභවයන්ගෙන් එකක් වන බැවිනි. 1983 දී ARM ISA හි මුල් නිර්මාණකරුවන්ගේ දීප්තිය වන ස්ටීව් ෆර්බර් සහ රොජර් (දැන් සොෆී) විල්සන් යන දෙදෙනාගේ දීප්තිය ඉක්මවා යා නොහැක.


1
ARM හි ඇති අනෙක් නවෝත්පාදනය නම්, S උපදෙස් උපසර්ගය එකතු කිරීම, (පාහේ) සියලු උපදෙස් මත විකල්පයක් වන අතර, එය නොමැති නම්, තත්ව බිටු වෙනස් කිරීමෙන් උපදෙස් වළක්වයි (CMP උපදෙස් හැරුණු විට, තත්ව බිටු සැකසීම ඔවුන්ගේ කාර්යය වේ, එබැවින් එයට S උපසර්ගය අවශ්‍ය නොවේ). මෙය බොහෝ අවස්ථාවන්හිදී CMP උපදෙස් මග හැරීමට ඉඩ සලසයි, සංසන්දනය ශුන්‍ය හෝ ඊට සමාන වන තාක් කල් (උදා: SUBS R0, R0, # 1 R0 ශුන්‍යයට ළඟා වන විට Z (ශුන්‍ය) බිට් එක සකසයි). කොන්දේසි සහ එස් උපසර්ගය ශුන්‍යයට ඉහළින් පවතී. එය තරමක් ලස්සන අයිඑස්ඒ ය.
ලූක් හචිසන්

2
එස් උපසර්ගය එකතු නොකිරීමෙන් ඔබට කොන්දේසි සහිත උපදෙස් කිහිපයක් පේළියක තබා ගැනීමට ඉඩ සලසයි, එයින් එකක් තත්ව බිටු වෙනස් කළ හැකි යැයි කරදර නොවී, අනෙක් කොන්දේසි සහිත උපදෙස් මඟ හැරීමේ අතුරු ආබාධයක් ඇති විය හැකිය.
ලූක් හචිසන්

OP ඒවායේ මිනුම් වර්ග කිරීමට ගතවන කාලය ඇතුළත් නොවන බව සලකන්න . X86 ලූප් ශාඛාවක් ධාවනය කිරීමට පෙර පළමුව වර්ග කිරීම සමස්ත අලාභයක් විය හැකිය, වර්ගීකරණය නොකල නඩුව මඟින් ලූපය බොහෝ සෙමින් ධාවනය කරයි. නමුත් විශාල පරාසයක තෝරා බේරා ගැනීමේ දී අවශ්ය ගොඩක් වැඩ.
පීටර් කෝර්ඩ්ස්

BTW, අරාවෙහි අවසානයට සාපේක්ෂව සුචිගත කිරීමෙන් ඔබට ලූපයේ උපදෙස් සුරැකිය හැක. ලූපයට පෙර, සකසන්න R2 = data + arraySize, ඉන්පසු ආරම්භ කරන්න R1 = -arraySize. ලූපයේ පතුල adds r1, r1, #1/ බවට පත්වේ bnz inner_loop. කිසියම් හේතුවක් නිසා සම්පාදකයින් මෙම ප්‍රශස්තිකරණය භාවිතා නොකරයි: / නමුත් කෙසේ වෙතත්, x86 වැනි වෙනත් ISA වල ශාඛා රහිත කේත සමඟ ඔබට කළ හැකි දේට වඩා මෙම අවස්ථාවේ දී එකතු කිරීම පුරෝකථනය කිරීම මූලික වශයෙන් වෙනස් නොවේ cmov. එය එතරම් හොඳ නැතත්: gcc ප්‍රශස්තිකරණ ධජය -O3 කේතය -O2 ට වඩා මන්දගාමී කරයි
පීටර් කෝඩ්ස්

1
(ARM සැබවින්ම උපදෙස්, ඒ නිසා ඔබ පවා දොස් බව පැටවුම් හෝ ගබඩා එය භාවිතා කළ හැකිය, මෙන් නොව, x86 NOPs ක්රියාත්මක පාදක cmovමතක ප්රභවය යන අගයද සමග AArch64 ඇතුළුව බොහෝ ISAs, පමණක් අලු මෙහෙයුම් තෝරා ඇත.. ඒ නිසා ARM predication බලවත් විය හැක, බොහෝ ISA වල ශාඛා රහිත කේත වලට වඩා කාර්යක්ෂමව භාවිතා කළ හැකිය.)
පීටර් කෝඩ්ස්

147

එය ශාඛා අනාවැකි ගැන ය. එය කුමක් ද?

  • ශාඛා පුරෝකථනය යනු නූතන ගෘහ නිර්මාණ ශිල්පයට අදාළ බව සොයා ගන්නා පුරාණ කාර්ය සාධනය වැඩි දියුණු කිරීමේ ක්‍රමවේදයන්ගෙන් එකකි. සරල පුරෝකථන ක්‍රම මඟින් වේගයෙන් බැලීමට සහ බලයේ කාර්යක්ෂමතාව ලබා දෙන අතර ඔවුන් ඉහළ වැරදි පුරෝකථන අනුපාතයකින් පීඩා විඳිති.

  • අනෙක් අතට, සංකීර්ණ ශාඛා අනාවැකි - ස්නායුක පදනම් වූ හෝ ද්වි-මට්ටමේ ශාඛා අනාවැකි වල ප්‍රභේද - වඩා හොඳ පුරෝකථන නිරවද්‍යතාව ලබා දෙයි, නමුත් ඒවා වැඩි බලයක් පරිභෝජනය කරන අතර සංකීර්ණතාව on ාතීය ලෙස වැඩි වේ.

  • මෙයට අමතරව, සංකීර්ණ පුරෝකථන ක්‍රමවේදයන් තුළ ශාඛා අනාවැකි කීමට ගතවන කාලය ඉතා ඉහළ මට්ටමක පවතී - චක්‍ර 2 සිට 5 දක්වා වෙනස් වේ - මෙය සැබෑ ශාඛා ක්‍රියාත්මක කිරීමේ කාලය හා සැසඳිය හැකිය.

  • ශාඛා පුරෝකථනය අත්‍යවශ්‍යයෙන්ම ප්‍රශස්තිකරණ (අවම කිරීමේ) ගැටළුවක් වන අතර එහිදී අවම මිස් අනුපාතය, අඩු බලශක්ති පරිභෝජනය සහ අවම සම්පත් සමඟ අඩු සංකීර්ණතාවයක් ළඟා කර ගැනීමට අවධාරණය කෙරේ.

සැබවින්ම විවිධ වර්ගයේ ශාඛා තුනක් ඇත:

ඉදිරි කොන්දේසි සහිත ශාඛා - ධාවන කාල කොන්දේසියක් මත පදනම්ව, PC (ක්‍රමලේඛ කවුන්ටරය) උපදෙස් ප්‍රවාහයේ ඉදිරි ලිපිනයකට යොමු කිරීම සඳහා වෙනස් කරනු ලැබේ.

පසුගාමී කොන්දේසි සහිත ශාඛා - උපදෙස් ප්‍රවාහයේ පසුපසට යොමු කිරීම සඳහා පරිගණකය වෙනස් කර ඇත. ශාඛාව පදනම් වී ඇත්තේ යම්කිසි කොන්දේසියක් මතය, එනම් වැඩසටහන් ලූපයක ආරම්භයට පසුපසට අතු බෙදීම වැනි ලූපයක් අවසානයේ ඇති පරීක්ෂණයකදී ලූපය නැවත ක්‍රියාත්මක කළ යුතු බව සඳහන් වේ.

කොන්දේසි විරහිත ශාඛා - මෙයට පැනීම්, ක්‍රියා පටිපාටි ඇමතුම් සහ නිශ්චිත කොන්දේසියක් නොමැති ප්‍රතිලාභ ඇතුළත් වේ. නිදසුනක් ලෙස, කොන්දේසි විරහිත පැනීමේ උපදෙස් එකලස් කිරීමේ භාෂාවෙන් හුදෙක් "jmp" ලෙස සංකේතවත් කළ හැකි අතර, උපදෙස් ප්‍රවාහය වහාම පැනීමේ උපදෙස් මඟින් පෙන්වා ඇති ඉලක්කගත ස්ථානයට යොමු කළ යුතු අතර, කොන්දේසි සහිත පැනීම "jmpne" ලෙස සංකේතවත් කළ හැකිය. පෙර "සංසන්දනය" උපදෙස් වල අගයන් දෙකක් සංසන්දනය කිරීමේ ප්‍රති result ල අගයන් සමාන නොවන බව පෙන්වන්නේ නම් පමණක් උපදෙස් ප්‍රවාහය හරවා යවනු ලැබේ. (X86 ගෘහ නිර්මාණ ශිල්පය භාවිතා කරන ඛණ්ඩිත ලිපින යෝජනා ක්‍රමය අමතර සංකීර්ණතාවයක් එක් කරයි, මන්ද පැනීම “ආසන්නයේ” (ඛණ්ඩයක් තුළ) හෝ “දුර” (කොටසට පිටතින්) විය හැකිය.

ස්ථිතික / ගතික ශාඛා පුරෝකථනය : කොන්දේසි සහිත ශාඛාවක් හමු වූ පළමු වරට ස්ථිතික ශාඛා පුරෝකථනය මයික්‍රොප්‍රොසෙසරය විසින් භාවිතා කරනු ලබන අතර කොන්දේසි සහිත ශාඛා කේතය සාර්ථකව ක්‍රියාත්මක කිරීම සඳහා ගතික ශාඛා පුරෝකථනය භාවිතා කරයි.

යොමුව:


146

ශාඛා අනාවැකිය ඔබට මන්දගාමී විය හැකිය යන කාරණයට අමතරව, වර්ග කළ අරාවකට තවත් වාසියක් ඇත:

වටිනාකම පරීක්ෂා කිරීම වෙනුවට ඔබට නැවතුම් තත්වයක් තිබිය හැකිය, මේ ආකාරයෙන් ඔබ අදාළ දත්ත පමණක් ලූප කර අනෙක් ඒවා නොසලකා හරින්න.
ශාඛා අනාවැකිය මග හැරෙන්නේ එක් වරක් පමණි.

 // 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
හරි, නමුත් අරාව වර්ග කිරීමේ සැකසුම් පිරිවැය O (N log N) වේ, එබැවින් ඔබ අරාව වර්ග කිරීමට ඇති එකම හේතුව වේලාසනින් බිඳ දැමිය හැකි නම් වේලාසනින් කැඩීම ඔබට උදව් නොකරයි. කෙසේ වෙතත්, අරාව කලින් වර්ග කිරීමට ඔබට වෙනත් හේතු තිබේ නම්, ඔව්, මෙය වටී.
ලූක් හචිසන්

ඔබ දත්ත කොපමණ වාර ගණනක් ලූපයක් සමඟ සසඳනවාද යන්න මත රඳා පවතී. මෙම උදාහරණයේ වර්ග කිරීම නිදසුනක් පමණි, එය
ලූපයට

2
ඔව්, මගේ පළමු අදහස් දැක්වීමේදී එය හරියටම එයයි :-) ඔබ කියන්නේ "ශාඛා අනාවැකිය මග හැරෙන්නේ එක් වරක් පමණි." නමුත් වර්ග කිරීමේ ඇල්ගොරිතම තුළ O (N log N) ශාඛා පුරෝකථනය මග හැරී ඇති බව ඔබ ගණන් නොගනී, එය ඇත්ත වශයෙන්ම O (N) ශාඛා අනාවැකි වලට වඩා විශාලයි. එබැවින්, වර්ග කළ ඇල්ගොරිතම මත පදනම්ව, (සමහර විට ඇත්ත වශයෙන්ම ඕ (10 ලොග් එන්) ට ආසන්නව, බිඳ දැමීම සඳහා වර්ග කළ දත්ත ඕ (ලොග් එන්) වාර ගණන භාවිතා කිරීමට ඔබට අවශ්‍ය වනු ඇත, උදා: ක්විච්සෝර්ට් සඳහා, හැඹිලි මිස් නිසා - ඒකාබද්ධ කිරීම වඩාත් හැඹිලි-සුසංයෝගී වේ, එබැවින් ඔබට ඕ (2 ලොග් එන්) භාවිතයන් පවා බිඳ දැමීමට අවශ්‍ය වේ.)
ලූක් හචිසන්

එක් වැදගත් ප්‍රශස්තිකරණයක් වනුයේ 127 හි ඉලක්කගත හැරීම් අගයට වඩා අඩු අයිතම පමණක් වර්ග කිරීම ("ඉක්මන් බාගයක්" පමණක් කිරීමයි ( හැරීමට පසුව හැරවීමට වඩා අඩු හෝ සමාන සියල්ල වර්ග කර ඇතැයි සිතමු). ඔබ හැරීම වෙත ළඟා වූ පසු, හැරීමට පෙර මූලද්‍රව්‍ය එකතු කරන්න. මෙය O (N log N) වෙනුවට O (N) ආරම්භක වේලාවේදී ක්‍රියාත්මක වනු ඇත, තවමත් ශාඛා අනාවැකි මග හැරීම් බොහෝමයක් ඇතත්, බොහෝ විට O (5 N) අනුපිළිවෙල අනුව මා කලින් ලබා දුන් සංඛ්‍යා මත පදනම්ව, එය බාගෙට බාගයක්.
ලූක් හචිසන්

132

ශාඛා පුරෝකථනය යනුවෙන් හැඳින්වෙන සංසිද්ධියක් හේතුවෙන් වර්ගීකරණය කරන ලද අරා වර්ගීකරණය නොකළ අරාවකට වඩා වේගයෙන් සකසනු ලැබේ.

ශාඛා පුරෝකථනය යනු ඩිජිටල් පරිපථයකි (පරිගණක ගෘහ නිර්මාණ ශිල්පයෙහි) ශාඛාවක් යන්නේ කුමන දිශාවටද යන්න අනාවැකි කීමට උත්සාහ කරමින් උපදෙස් නල මාර්ගයේ ප්‍රවාහය වැඩි දියුණු කරයි. පරිපථය / පරිගණකය ඊළඟ පියවර පුරෝකථනය කර එය ක්‍රියාත්මක කරයි.

වැරදි පුරෝකථනයක් කිරීම පෙර පියවර වෙත ආපසු යාමට සහ වෙනත් පුරෝකථනයකින් ක්‍රියාත්මක කිරීමට මඟ පාදයි. පුරෝකථනය නිවැරදි යැයි උපකල්පනය කිරීම, කේතය ඊළඟ පියවර දක්වා ඉදිරියට යනු ඇත. වැරදි පුරෝකථනයක් නිවැරදි පුරෝකථනයක් සිදුවන තුරු එකම පියවර නැවත නැවත සිදු කරයි.

ඔබේ ප්‍රශ්නයට පිළිතුර ඉතා සරල ය.

වර්ගීකරණය නොකළ අරාව තුළ, පරිගණකය විවිධ අනාවැකි පළ කරන අතර එමඟින් දෝෂ ඇතිවීමේ ඉඩකඩ වැඩිය. කෙසේ වෙතත්, වර්ග කළ අරාවකින් පරිගණකය අනාවැකි අඩු කරයි, දෝෂ ඇතිවීමේ අවස්ථාව අඩු කරයි. වැඩි අනාවැකි පළ කිරීමට වැඩි කාලයක් අවශ්‍ය වේ.

වර්ග කළ අරාව: කෙළින් පාර ________________________________________________________________________________ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

වර්ගීකරණය නොකළ අරාව: වක්‍ර පාර

______   ________
|     |__|

ශාඛා පුරෝකථනය: කුමන මාර්ගය කෙළින්දැයි අනුමාන කිරීම / පුරෝකථනය කිරීම සහ පරීක්ෂා කිරීමකින් තොරව එය අනුගමනය කිරීම

___________________________________________ Straight road
 |_________________________________________|Longer road

මාර්ග දෙකම එකම ගමනාන්තයට ළඟා වුවද, සෘජු මාර්ගය කෙටි වන අතර අනෙක දිගු වේ. එසේනම් ඔබ අනෙකා වැරදීමකින් තෝරා ගන්නේ නම්, ආපසු හැරීමක් සිදු නොවේ, එබැවින් ඔබ දිගු මාර්ගය තෝරා ගන්නේ නම් අමතර කාලයක් නාස්ති කරනු ඇත. මෙය පරිගණකයේ සිදුවන දෙයට සමාන වන අතර මෙය වඩාත් හොඳින් තේරුම් ගැනීමට ඔබට උපකාරී වනු ඇතැයි මම බලාපොරොත්තු වෙමි.


එසේම අදහස් දැක්වීම් වලින් im සිමොන්_වීවර් උපුටා දැක්වීමට මට අවශ්‍යය :

එය අනාවැකි අඩු නොකරයි - එය වැරදි අනාවැකි අඩු කරයි. එය තවමත් ලූපය හරහා එක් එක් කාලයට අනාවැකි කිව යුතුය ...


124

පහත දැක්වෙන MATLAB කේතය සඳහා මගේ මැක්බුක් ප්‍රෝ (Intel i7, 64 bit, 2.4 GHz) සමඟ MATLAB 2011b සමඟ එකම කේතය උත්සාහ කළෙමි:

% 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;

ඉහත MATLAB කේතය සඳහා ප්‍රති results ල පහත පරිදි වේ:

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

CodeGManNickG හි ඇති පරිදි C කේතයේ ප්‍රති results ල මට ලැබේ:

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

මේ මත පදනම්ව, මැට්ලැබ් වර්ග කිරීමකින් තොරව සී ක්‍රියාත්මක කිරීමට වඩා 175 ගුණයකින් මන්දගාමී වන අතර වර්ග කිරීම සමඟ 350 ගුණයකින් මන්දගාමී වේ. වෙනත් වචන වලින් කිවහොත්, (ශාඛා අනාවැකිය) ඒ ටිකම තමයි 1.46x MATLAB ක්රියාත්මක කිරීම සඳහා 2.7x සී ක්රියාත්මක කිරීම සඳහා.


7
සම්පුර්ණත්වය උදෙසා, මෙය බොහෝ විට ඔබ මැට්ලැබ් හි ක්‍රියාත්මක කරන්නේ කෙසේද යන්න නොවේ. ගැටලුව දෛශිකකරණය කිරීමෙන් පසුව සිදු කළ හොත් එය වඩා වේගවත් වනු ඇතැයි මම සිතමි.
ysap

1
මැට්ලැබ් බොහෝ අවස්ථාවන්හිදී ස්වයංක්‍රීය සමාන්තරකරණය / දෛශිකකරණය සිදු කරයි, නමුත් මෙහි ඇති ගැටළුව වන්නේ ශාඛා අනාවැකි වල බලපෑම පරීක්ෂා කිරීමයි. මැට්ලැබ් කෙසේ වෙතත් ප්‍රතිශක්තීකරණයක් නැත!
ෂාන්

1
මැට්ලැබ් ස්වදේශීය අංක හෝ මැට් විද්‍යාගාර විශේෂිත ක්‍රියාත්මක කිරීමක් භාවිතා කරයි (අසීමිත ඉලක්කම් ප්‍රමාණයක් හෝ
එසේද

55

දත්ත වර්ග කිරීම සඳහා යමෙකුට අවශ්‍ය යැයි වෙනත් පිළිතුරු මගින් උපකල්පනය කිරීම නිවැරදි නොවේ.

පහත කේතය සමස්ත අරාව වර්ග නොකරයි, නමුත් එහි මූලද්‍රව්‍ය 200 ක් පමණක් වන අතර එමඟින් වේගවත්ම ධාවනය වේ.

K- මූලද්‍රව්‍ය කොටස් පමණක් වර්ග කිරීම, සම්පූර්ණ අරාව වර්ග කිරීමට අවශ්‍ය කාලයට 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
මෙය කිසිවක් ඔප්පු කරන්නේ කෙසේදැයි මට නොපෙනේ? ඔබ පෙන්වා ඇති එකම දෙය නම්, “මුළු අරාව වර්ග කිරීමේ සියලු කාර්යයන් නොකිරීමට මුළු අරාව වර්ග කිරීමට වඩා අඩු කාලයක් ගත වේ” යන්නයි. මෙය “වේගයෙන් ද ක්‍රියාත්මක වේ” යන ඔබේ ප්‍රකාශය ගෘහ නිර්මාණ ශිල්පය මත රඳා පවතී. ARM හි මෙය ක්‍රියාත්මක වන ආකාරය පිළිබඳ මගේ පිළිතුර බලන්න. PS ඔබට මූලද්‍රව්‍ය 200 ක බ්ලොක් ලූපය තුළ සාරාංශය තැබීමෙන්, ප්‍රතිලෝමව වර්ග කිරීමෙන් සහ පරාසය ඉක්මවා ගිය වටිනාකමක් ලබා ගත් පසු බිඳ දැමීමේ යොචායි ටිමර්ගේ යෝජනාව භාවිතා කිරීමෙන් ARM නොවන ගෘහ නිර්මාණ මත ඔබේ කේතය වේගවත් කළ හැකිය. මේ අනුව සෑම මූලද්‍රව්‍ය 200 ක වාරණ සාරාංශයක්ම කලින් අවසන් කළ හැකිය.
ලූක් හචිසන්

වර්ගීකරණය නොකළ දත්ත වලට වඩා ඇල්ගොරිතම කාර්යක්ෂමව ක්‍රියාත්මක කිරීමට ඔබට අවශ්‍ය නම්, ඔබ එම ක්‍රියාව ශාඛා රහිතව සිදු කරනු ඇත (සහ සිම්ඩ් සමඟ, උදා: x86 pcmpgtbසමඟ ඒවායේ ඉහළ බිට් කට්ටලය සහිත මූලද්‍රව්‍ය සොයා ගැනීමට සහ කුඩා මූලද්‍රව්‍ය ශුන්‍ය කිරීමට). කුට්ටි වර්ග කිරීම ඕනෑම වේලාවක ගත කිරීම මන්දගාමී වේ. ශාඛා රහිත අනුවාදයකට දත්ත-ස්වාධීන කාර්ය සාධනයක් ඇති අතර, පිරිවැය ශාඛා වැරදි අනාවැකි මගින් සිදු වූ බව සනාථ කරයි. නැතහොත් ස්කයිලේක් වැනි සෘජුවම නිරීක්ෂණය කිරීමට int_misc.clear_resteer_cyclesහෝ වැරදි int_misc.recovery_cyclesඅනාවැකි වලින් ඉදිරිපස අක්‍රිය චක්‍ර ගණන් කිරීමට කාර්ය සාධන කවුන්ටර භාවිතා කරන්න
පීටර් කෝඩ්ස්

ඉහත අදහස් දෙකම සාමාන්‍ය ඇල්ගොරිතම ගැටළු සහ සංකීර්ණතාවයන් නොසලකා හරින බව පෙනේ, විශේෂ යන්ත්‍ර උපදෙස් සමඟ විශේෂිත දෘඩාංග වෙනුවෙන් පෙනී සිටීමට. විශේෂිත යන්ත උපදෙස් වලට අන්ධව අනුග්‍රහය දක්වමින් මෙම පිළිතුරේ ඇති වැදගත් පොදු අවබෝධය අවිනිශ්චිත ලෙස බැහැර කරන පළමු එක විශේෂයෙන් සුලු බව මට පෙනේ.
user2297550

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]) …”හෝ ඊට සමාන ය. වර්ග කළ අනුක්‍රමයක් සඳහා එම පරීක්ෂණය සැමවිටම සත්‍ය වන අතර අහඹු අනුක්‍රමයක් සඳහා තෝරාගත් ශාඛාව අහඹු ලෙස වෙනස් වේ.

තවත් හේතුවක් නම්, දෛශිකය දැනටමත් වර්ග කර ඇති විට, අපට කිසි විටෙකත් මූලද්‍රව්‍ය ඒවායේ නිවැරදි ස්ථානයට ගෙනයාමට අවශ්‍ය නොවේ. මෙම කුඩා විස්තර වල බලපෑම අප දුටු පහක් හෝ හයක සාධකයයි.

ක්වික්සෝර්ට් (සහ පොදුවේ වර්ග කිරීම) යනු පරිගණක විද්‍යාවේ ශ්‍රේෂ් greatest තම මනස ආකර්ෂණය කර ගත් සංකීර්ණ අධ්‍යයනයකි. හොඳ වර්ගීකරණ ශ්‍රිතයක් යනු හොඳ ඇල්ගොරිතමයක් තෝරා ගැනීම සහ එය ක්‍රියාත්මක කිරීමේදී දෘඩාංග ක්‍රියාකාරිත්වය කෙරෙහි අවධානය යොමු කිරීමයි.

ඔබට කාර්යක්ෂම කේතයක් ලිවීමට අවශ්‍ය නම්, යන්ත්‍ර සැකැස්ම පිළිබඳව ඔබ ටිකක් දැන සිටිය යුතුය.


28

මෙම ප්‍රශ්නය CPU වල ශාඛා පුරෝකථන ආකෘති වල මුල් බැස ඇත. මෙම ලිපිය කියවීමට මම නිර්දේශ කරමි:

බහු ශාඛා පුරෝකථනය සහ ශාඛා ලිපින හැඹිලිය හරහා උපදෙස් ලබා ගැනීමේ අනුපාතය වැඩි කිරීම

ඔබ මූලද්‍රව්‍ය වර්ග කර ඇති විට, සියලු CPU උපදෙස් ලබා ගැනීමට IR ට කරදර විය නොහැක, නැවත නැවතත්, එය ඒවා හැඹිලියෙන් ලබා ගනී.


වැරදි අනාවැකි නොසලකා CPU හි L1 උපදෙස් හැඹිලියේ උපදෙස් උණුසුම්ව පවතී. ගැටළුව වන්නේ වහාම පෙර උපදෙස් විකේතනය කර ක්‍රියාත්මක කිරීම අවසන් කිරීමට පෙර ඒවා නිසි පිළිවෙළට නල මාර්ගයට ගෙන ඒමයි .
පීටර් කෝර්ඩ්ස්

15

ශාඛා පුරෝකථන දෝෂ වළක්වා ගත හැකි එක් ක්‍රමයක් නම් බැලීමේ වගුවක් තැනීම සහ දත්ත භාවිතා කර එය සුචිගත කිරීමයි. ස්ටෙෆාන් ඩි බ ru යින් ඔහුගේ පිළිතුරෙන් ඒ ගැන සාකච්ඡා කළේය.

නමුත් මේ අවස්ථාවේ දී, අගයන් [0, 255] පරාසයේ පවතින බව අපි දනිමු. අපි සැලකිලිමත් වන්නේ සාරධර්ම> = 128 ගැන පමණි. එයින් අදහස් කරන්නේ අපට වටිනාකමක් අවශ්‍යද නැද්ද යන්න අපට පවසන තනි බිට් එකක් පහසුවෙන් උකහා ගත හැකි බවයි: මාරුවීමෙන් දත්ත දකුණු බිටු 7 ට, අපට ඉතිරිව ඇත්තේ බිට් 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];

මෙම කේතය එකතු කිරීම් වලින් අඩක් නාස්ති කරන නමුත් කිසි විටෙකත් ශාඛා අනාවැකි අසමත් නොවේ. සත්‍ය නම් ප්‍රකාශයක් සහිත අනුවාදයට වඩා අහඹු දත්ත මත එය ඉතා වේගවත් වේ.

නමුත් මගේ පරීක්ෂණයේදී, පැහැදිලි බැලීමේ වගුවක් මීට වඩා තරමක් වේගවත් විය, බොහෝ විට බැලීමේ වගුවකට සුචිගත කිරීම බිට් මාරුවට වඩා මඳක් වේගවත් විය. මෙයින් පෙන්නුම් කරන්නේ මගේ කේතය සැකසීමේ සහ බැලීමේ වගුව භාවිතා කරන ආකාරයයි (කේතයේ "ලුක්අප් වගුව" සඳහා නොසිතූ ලෙස ලුට් ලෙස හැඳින්වේ). මෙන්න C ++ කේතය:

// ප්‍රකාශ කර බලා බැලීමේ වගුව පුරවන්න

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;
this library would do something like:

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

එය හොඳ විසඳුමක් විය හැකිය සමහර විට එය ක්‍රියාත්මක වනු ඇත


ඔබ මෙය පරීක්ෂා කළේ කුමන C ++ සම්පාදක / දෘඩාංගද, සහ කුමන සම්පාදක විකල්ප සමඟද? මට පුදුමයි මුල් අනුවාදය අතු රහිත සිම් කේතයට ස්වයංක්‍රීයව දෛශිකකරණය නොකිරීම. ඔබ සම්පූර්ණ ප්‍රශස්තිකරණය සක්‍රීය කළාද?
පීටර් කෝර්ඩ්ස්

4096 ප්‍රවේශ විමසුම් වගුවක් උමතුවෙන් මෙන් පෙනේ. ඔබ කිසියම් බිටු මාරු කරන්නේ නම් , ඔබට මුල් අංකය එක් කිරීමට අවශ්‍ය නම් පමණක් LUT ප්‍රති result ලය භාවිතා කළ නොහැක . ශාඛා රහිත ශිල්පීය ක්‍රම පහසුවෙන් භාවිතා නොකොට ඔබේ සම්පාදකයා වටා වැඩ කිරීමට මේ සියල්ල මෝඩ උපක්‍රම මෙන් පෙනේ. වඩාත් සරල වනු ඇත්තේ mask = tmp < 128 : 0 : -1UL;/total += tmp & mask;
පීටර් කෝඩ්ස්
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.