c++ - <<Faster than<=?




performance assembly (9)

আমি এমন একটি বই পড়ছি যেখানে লেখক বলছেন যে if( a < 901 ) চেয়ে দ্রুত হয় if( a <= 900 )

ঠিক এই সহজ উদাহরণের মতো নয়, তবে লুপ জটিল কোডটিতে সামান্য কর্মক্ষমতা পরিবর্তন রয়েছে। আমি মনে করি এটি জেনারেটেড কোড কোডের সাথে কিছু করার থাকলেও এটি সত্য।


TL;DR উত্তর

আর্কিটেকচার, কম্পাইলার এবং ভাষা অধিকাংশ সমন্বয় জন্য এটি দ্রুত হবে না।

সম্পূর্ণ উত্তর

অন্যান্য উত্তরগুলি x86 আর্কিটেকচারের উপর মনোনিবেশ করেছে এবং আমি ARM আর্কিটেকচারটি জানি না (যা আপনার উদাহরণ অ্যাসবেলার মনে হচ্ছে) বিশেষভাবে কোড তৈরির জন্য বিশেষভাবে মন্তব্য করতে যথেষ্ট, তবে এটি একটি micro-optimisation যা একটি খুব আর্কিটেকচারের উদাহরণ। নির্দিষ্ট, এবং এটি একটি অপ্টিমাইজেশান হিসাবে এটি একটি বিরোধী-অপ্টিমাইজেশান হতে পারে

এইভাবে, আমি সুপারিশ করব যে micro-optimisation এই ধরণের সেরা সফ্টওয়্যার প্রকৌশল অনুশীলনের পরিবর্তে কারগো সংস্কৃতির উদাহরণ।

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

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

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


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


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

যদি কিছু প্ল্যাটফর্ম ছিল যেখানে < <= সহজ পূর্ণসংখ্যার ধরনগুলির চেয়ে <= দ্রুততর ছিল, কম্পাইলার সর্বদা <= থেকে < জন্য রূপান্তর করতে হবে। যে কোন কম্পাইলার যা শুধুমাত্র একটি খারাপ কম্পাইলার (যে প্ল্যাটফর্মের জন্য) হবে না।


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

if(a < 901)
cmpl  $900, -4(%rbp)
jg .L2

if(a <=901)
cmpl  $901, -4(%rbp)
jg .L3

লিনাক্সে x86_64 প্ল্যাটফর্মের উপর জিसीसी থেকে আমার উদাহরণ।

কম্পাইলার লেখক বেশ স্মার্ট মানুষ, এবং তারা এই জিনিস এবং অনেক অন্যদের আমাদের জন্য মঞ্জুরি নিতে মনে করেন।

আমি লক্ষ্য করেছি যে এটি ধ্রুবক না হলে, একই ক্ষেত্রে একই মেশিন কোড তৈরি করা হয়।

int b;
if(a < b)
cmpl  -4(%rbp), %eax
jge   .L2

if(a <=b)
cmpl  -4(%rbp), %eax
jg .L3

ঐতিহাসিকভাবে (আমরা 1980 এর দশকের শুরুতে এবং 1990 এর দশকের গোড়ার দিকে কথা বলছি), কিছু স্থাপত্য ছিল যেখানে এটি সত্য ছিল। রুট ইস্যু হল পূর্ণসংখ্যা তুলনা স্বতঃস্ফূর্তভাবে পূর্ণসংখ্যা বিয়োগের মাধ্যমে প্রয়োগ করা হয়। এই নিম্নলিখিত ক্ষেত্রে বৃদ্ধি দেয়।

Comparison     Subtraction
----------     -----------
A < B      --> A - B < 0
A = B      --> A - B = 0
A > B      --> A - B > 0

এখন, যখন A < B বিয়োগকে বিয়োগের জন্য একটি উচ্চ-বিট ধার করতে হয়, ঠিক যেমন আপনি নিজের হাতে যোগ এবং বিয়োগ করার সময় বহন করেন এবং ধার দেন। এই "ধার" বিট সাধারণত বহন বিট হিসাবে বলা হয় এবং একটি শাখা নির্দেশ দ্বারা testable হবে। শূন্য বিট নামক দ্বিতীয় বিট সেট করা হবে যদি বিয়োগটি সমানভাবে শূন্য ছিল যা সমতা বোঝায়।

সাধারণত কমপক্ষে দুটি শর্তাধীন শাখা নির্দেশাবলী ছিল, এক বহন বিট শাখা এবং শূন্য বিট এক।

এখন, বিষয়টির অন্তরে পৌঁছানোর জন্য, বহন এবং শূন্য বিট ফলাফলগুলি অন্তর্ভুক্ত করতে পূর্ববর্তী সারণিকে প্রসারিত করুন।

Comparison     Subtraction  Carry Bit  Zero Bit
----------     -----------  ---------  --------
A < B      --> A - B < 0    0          0
A = B      --> A - B = 0    1          1
A > B      --> A - B > 0    1          0

সুতরাং, A < B জন্য একটি শাখা বাস্তবায়ন করা যেতে পারে এক নির্দেশনায়, কারণ বহন বিট শুধুমাত্র এই ক্ষেত্রেই স্পষ্ট, অর্থাৎ,

;; Implementation of "if (A < B) goto address;"
cmp  A, B          ;; compare A to B
bcz  address       ;; Branch if Carry is Zero to the new address

কিন্তু, যদি আমরা কম বা সমান তুলনা করতে চাই, তাহলে সমতার ক্ষেত্রে ধরা পড়ার জন্য আমাদের জিরো ফ্ল্যাগের অতিরিক্ত চেক করতে হবে।

;; Implementation of "if (A <= B) goto address;"
cmp A, B           ;; compare A to B
bcz address        ;; branch if A < B
bzs address        ;; also, Branch if the Zero bit is Set

সুতরাং, কিছু মেশিনে, "কম" তুলনা ব্যবহার করে একটি মেশিন নির্দেশ সংরক্ষণ করতে পারে । এটি সাব মেগাহার্টজ প্রসেসর গতি এবং 1: 1 CPU-to-memory গতি অনুপাতের যুগে প্রাসঙ্গিক ছিল, তবে এটি প্রায় পুরোপুরি অপ্রাসঙ্গিক।


খুব কম সময়ে, যদি এটি সত্য হয় তবে একটি কম্পাইলারটি <= b থেকে! (A> b) টিকটিকভাবে অপ্টিমাইজ করতে পারে এবং তাই যদি তুলনাটি আসলেই ধীরে ধীরে ধীর হয়ে থাকে তবে সর্বাধিক অসহায় কম্পাইলারের সাথে আপনি কোন পার্থক্য দেখবেন না ।


না, এটি বেশিরভাগ আর্কিটেকচারে দ্রুত হবে না। আপনি উল্লেখ করেন নি, কিন্তু x86 এ, সমস্ত অবিচ্ছেদ্য তুলনাগুলি সাধারণত দুটি মেশিন নির্দেশাবলীতে প্রয়োগ করা হবে:

  • একটি test বা EFLAGS নির্দেশ, যা EFLAGS সেট
  • এবং তুলনা (এবং কোড লেআউট) উপর নির্ভর করে, একটি Jcc (লাফ) নির্দেশ :
    • jne - যদি সমান না লাফ -> ZF = 0
    • jz - শূন্য (সমান) -> ZF = 1 ঝাঁপ দাও
    • jg - বৃহত্তর যদি ঝাঁপ দাও -> ZF = 0 and SF = OF
    • (ইত্যাদি ...)

উদাহরণ (সংক্ষিপ্তত্বের জন্য সম্পাদিত) $ gcc -m32 -S -masm=intel test.c

    if (a < b) {
        // Do something 1
    }

সংকলন:

    mov     eax, DWORD PTR [esp+24]      ; a
    cmp     eax, DWORD PTR [esp+28]      ; b
    jge     .L2                          ; jump if a is >= b
    ; Do something 1
.L2:

এবং

    if (a <= b) {
        // Do something 2
    }

সংকলন:

    mov     eax, DWORD PTR [esp+24]      ; a
    cmp     eax, DWORD PTR [esp+28]      ; b
    jg      .L5                          ; jump if a is > b
    ; Do something 2
.L5:

সুতরাং দুটি মধ্যে শুধুমাত্র পার্থক্য একটি jge বনাম একটি jge নির্দেশনা। দুই সময় একই সময় নিতে হবে।

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

Latency - একটি নির্দেশ গঠনকারী সমস্ত μops নির্বাহ সম্পূর্ণ করার জন্য কার্যকর কোরের জন্য প্রয়োজনীয় ঘড়ির চক্রগুলির সংখ্যা।

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

Jcc এর জন্য মানগুলি হল:

      Latency   Throughput
Jcc     N/A        0.5

Jcc নিম্নলিখিত পাদটীকা সঙ্গে:

7) শর্তসাপেক্ষ জাম্প নির্দেশনা নির্বাচন শাখার পূর্বাভাসযোগ্যতা উন্নত করতে বিভাগ 3.4.1, "শাখা পূর্বাভাস অপ্টিমাইজেশান" এর সুপারিশের উপর ভিত্তি করে করা উচিত। যখন শাখার সফলভাবে পূর্বাভাস দেওয়া হয়, তখন jcc এর jcc কার্যকরী শূন্য।

সুতরাং, ইন্টেল ডক্সের কোন কিছুই অন্যের থেকে অন্য কোনও Jcc নির্দেশের সাথে কখনও আচরণ করে না।

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

সম্পাদনা: ভাসমান পয়েন্ট

এটি x87 ভাসমান বিন্দুর জন্যও সত্য বলে ধরে রাখে: (উপরে যেমন খুব বেশি কোড, কিন্তু int পরিবর্তে double ।)

        fld     QWORD PTR [esp+32]
        fld     QWORD PTR [esp+40]
        fucomip st, st(1)              ; Compare ST(0) and ST(1), and set CF, PF, ZF in EFLAGS
        fstp    st(0)
        seta    al                     ; Set al if above (CF=0 and ZF=0).
        test    al, al
        je      .L2
        ; Do something 1
.L2:

        fld     QWORD PTR [esp+32]
        fld     QWORD PTR [esp+40]
        fucomip st, st(1)              ; (same thing as above)
        fstp    st(0)
        setae   al                     ; Set al if above or equal (CF=0).
        test    al, al
        je      .L5
        ; Do something 2
.L5:
        leave
        ret

প্রকৃতপক্ষে, তারা ঠিক একই গতিতে থাকবে, কারণ সমাবেশ পর্যায়ে তারা উভয় একটি লাইন গ্রহণ। যেমন:

  • jl ax,dx (AX যদি DX এর চেয়ে কম হয় তবে ঝাঁপ দাও)
  • jle ax,dx (AX যদি DX এর চেয়ে কম বা সমান হয়)

তাই না, দ্রুত না। কিন্তু যদি আপনি সত্যিই প্রযুক্তিগত পেতে চান তবে আমি মনে করি যদি আপনি ইলেকট্রন বর্তমান স্তরে এটি পরীক্ষা করে দেখতে চান তবে এটি আরও দ্রুত হবে তবে আপনি যে গতিতে লক্ষ্য করবেন তার কাছাকাছি কোনও স্থান নেই।


সম্ভবত সেই অনামী বইটির লেখকটি পড়েন যে>> a > 0 a >= 1 চেয়ে দ্রুততর গতিতে এবং সর্বজনীনভাবে সত্য বলে মনে করে।

কিন্তু এটি একটি কারণ জড়িত কারণ ( CMP , আর্কিটেকচারের উপর নির্ভর করে, যেমন OR দিয়ে প্রতিস্থাপিত হতে পারে) এবং এর কারণে নয় <





relational-operators