যখন আমি এই উত্তরটি লিখেছি, আমি কেবল <বনাম <= সাধারণভাবে শিরোনাম প্রশ্নটি দেখছিলাম, ধ্রুবক a < 901
বনামের নির্দিষ্ট উদাহরণ নয় a <= 900
। অনেকগুলি সংকলক সর্বদা <
এবং এর মধ্যে রূপান্তর করে ধ্রুবকগুলির <=
পরিমাণকে সঙ্কুচিত করে , যেমন x86 তাত্ক্ষণিক অপারেন্ডারে -128..127 এর জন্য 1-বাইটের সংক্ষিপ্ত আকার রয়েছে।
এআরএম এবং বিশেষত এআরচ For৪ এর জন্য, তাত্ক্ষণিকভাবে এনকোড করতে সক্ষম হওয়া কোনও শব্দের কোনও সংকীর্ণ ক্ষেত্রকে কোনও অবস্থানে ঘোরাতে সক্ষম হওয়ার উপর নির্ভর করে। সুতরাং cmp w0, #0x00f000
এনকোডেবল cmp w0, #0x00effff
হতে পারে , এবং নাও হতে পারে। সুতরাং তুলনা করার জন্য মেক-ইট-ছোট নিয়ম বনাম একটি সংকলন-সময় ধ্রুবক সর্বদা AArch64 এর জন্য প্রযোজ্য নয়।
<বনাম << সাধারণভাবে রানটাইম-পরিবর্তনশীল শর্ত সহ
বেশিরভাগ মেশিনে অ্যাসেম্বলি ভাষায়, তুলনার জন্য একটি তুলনা <=
একই ব্যয় হয় <
। আপনি এটিতে শাখা করছেন কিনা, 0/1 পূর্ণসংখ্যা তৈরি করতে এটি বুলিয়েইন করে বা ব্রাঞ্চবিহীন সিলেক্ট অপারেশনের (প্র x x সিএমওভের মতো) প্রিডিকেট হিসাবে ব্যবহার করে তা প্রয়োগ করে। অন্যান্য উত্তরগুলি কেবল প্রশ্নের এই অংশটিকেই সম্বোধন করেছে।
তবে এই প্রশ্নটি সি ++ অপারেটরগুলি সম্পর্কে, অপ্টিমাইজারের ইনপুট । সাধারণত তারা উভয়ই সমান দক্ষ; বইয়ের পরামর্শগুলি পুরোপুরি বগাস বলে মনে হচ্ছে কারণ সংকলকরা সর্বদা তুলনাকে রূপান্তর করতে পারে যা তারা asm এ প্রয়োগ করে। তবে ব্যবহার করার ক্ষেত্রে অন্তত একটি ব্যতিক্রম রয়েছে<=
দুর্ঘটনাক্রমে এমন কিছু তৈরি করতে পারে যা সংকলক অনুকূলিত করতে পারে না।
একটি লুপ শর্ত হিসাবে, মামলা আছে কোথায় আছেন তা <=
হল গুণগতভাবে থেকে আলাদা <
, যখন এটি প্রতিপাদন করে একটি লুপ অসীম নয় থেকে কম্পাইলার স্টপ। এটি অটো-ভেক্টরাইজেশন অক্ষম করে একটি বড় পার্থক্য আনতে পারে।
স্বাক্ষরযুক্ত ওভারফ্লোটি স্বাক্ষরিত ওভারফ্লো (ইউবি) এর বিপরীতে চারদিকে বেস -২ মোড়ানো হিসাবে ভাল সংজ্ঞায়িত। স্বাক্ষরযুক্ত লুপ কাউন্টারগুলি সাধারণত সংকলকগুলির সাথে এটি থেকে নিরাপদ থাকে যা স্বাক্ষরিত-ওভারফ্লো ইউবি না হওয়ার উপর ভিত্তি করে অনুকূলিত করে: ++i <= size
সর্বদা মিথ্যা হয়ে যাবে। ( প্রতিটি সি প্রোগ্রামার অপরিশোধিত আচরণ সম্পর্কে কী জেনে রাখা উচিত )
void foo(unsigned size) {
unsigned upper_bound = size - 1; // or any calculation that could produce UINT_MAX
for(unsigned i=0 ; i <= upper_bound ; i++)
...
সংকলকগণ কেবলমাত্র অনির্ধারিত আচরণের দিকে পরিচালিত করে, সমস্ত সম্ভাব্য ইনপুট মানগুলির জন্য সি ++ উত্সের (সংজ্ঞায়িত এবং আইনত পর্যবেক্ষণযোগ্য) আচরণ সংরক্ষণের উপায়গুলিতে কেবল অনুকূল করতে পারেন ।
(একটি সাধারণ i <= size
সমস্যাটিও তৈরি করবে, তবে আমি ভেবেছিলাম যে একটি উচ্চতর গণ্ডী গণনা করা দুর্ঘটনাক্রমে এমন কোনও ইনপুট যা আপনার যত্ন নেয় না সেটির জন্য অসীম লুপের সম্ভাবনা প্রবর্তনের আরও বাস্তব উদাহরণ যা সংকলকটি অবশ্যই বিবেচনা করবে))
এই ক্ষেত্রে, size=0
বাড়ে upper_bound=UINT_MAX
এবং i <= UINT_MAX
সর্বদা সত্য। সুতরাং এই লুপটি অসীম size=0
, এবং সংকলকটিকে সম্মান করতে হবে যদিও আপনি প্রোগ্রামার হিসাবে সম্ভবত কখনও কখনও আকার = 0 পাস করার ইচ্ছা করে না। সংকলক যদি এই ফাংশনটিকে কলারের সাথে ইনলাইন করতে পারে যেখানে এটি প্রমাণ করতে পারে যে আকার = 0 অসম্ভব, তবে দুর্দান্ত, এটি এটি যেমনটি করতে পারে তেমন অনুকূল করতে পারে i < size
।
লুটের অভ্যন্তরে সত্যিকারের মান প্রয়োজন না হলে এএসএম লাইক if(!size) skip the loop;
do{...}while(--size);
একটি for( i<size )
লুপকে অনুকূলকরণের একটি সাধারণভাবে কার্যকর উপায় i
( কেন লুপগুলি সর্বদা "ডু ... যখন" স্টাইল (টেল জাম্প) তে সংকলিত হয়? )।
তবে এটি অসীম হতে পারে না এমন সময়টি করে: যদি প্রবেশ করে তবে size==0
আমরা 2 ite n পুনরাবৃত্তি পাই। ( লুপ সি এর জন্য স্বাক্ষরযুক্ত সমস্ত পূর্ণসংখ্যার উপরে আইট্রেট করা শূন্য সহ সমস্ত স্বাক্ষরিত পূর্ণসংখ্যার উপর একটি লুপ প্রকাশ করা সম্ভব করে তোলে, তবে যেমন বহনকারী পতাকাটি এটি asm তে থাকে তেমন সহজ নয়))
লুপের কাউন্টারটি মোছার সম্ভাবনা রয়েছে বলে আধুনিক সংকলকরা প্রায়শই কেবল "হাল ছেড়ে" দেন এবং প্রায় আক্রমণাত্মকভাবে অনুকূলিত হন না।
উদাহরণ: 1 থেকে n পর্যন্ত পূর্ণসংখ্যার যোগফল
স্বাক্ষরবিহীন স্বাক্ষর ব্যবহারের মাধ্যমে i <= n
ঝাঁকুনির আইডিয়োম-স্বীকৃতি যাsum(1 .. n)
গৌসের n * (n+1) / 2
সূত্রের ভিত্তিতে বন্ধ ফর্মের সাথে লুপগুলি অনুকূল করে ।
unsigned sum_1_to_n_finite(unsigned n) {
unsigned total = 0;
for (unsigned i = 0 ; i < n+1 ; ++i)
total += i;
return total;
}
গডবোল্ট সংকলক এক্সপ্লোরারের ক্ল্যাং 7.0 এবং জিসিসি 8.2 থেকে x86-64 এএসএম
# clang7.0 -O3 closed-form
cmp edi, -1 # n passed in EDI: x86-64 System V calling convention
je .LBB1_1 # if (n == UINT_MAX) return 0; // C++ loop runs 0 times
# else fall through into the closed-form calc
mov ecx, edi # zero-extend n into RCX
lea eax, [rdi - 1] # n-1
imul rax, rcx # n * (n-1) # 64-bit
shr rax # n * (n-1) / 2
add eax, edi # n + (stuff / 2) = n * (n+1) / 2 # truncated to 32-bit
ret # computed without possible overflow of the product before right shifting
.LBB1_1:
xor eax, eax
ret
তবে নিষ্পাপ সংস্করণের জন্য, আমরা কেবল ঝাঁকুনি থেকে একটি বোবা লুপ পাই।
unsigned sum_1_to_n_naive(unsigned n) {
unsigned total = 0;
for (unsigned i = 0 ; i<=n ; ++i)
total += i;
return total;
}
# clang7.0 -O3
sum_1_to_n(unsigned int):
xor ecx, ecx # i = 0
xor eax, eax # retval = 0
.LBB0_1: # do {
add eax, ecx # retval += i
add ecx, 1 # ++1
cmp ecx, edi
jbe .LBB0_1 # } while( i<n );
ret
জিসিসি কোনওভাবেই ক্লোজড ফর্ম ব্যবহার করে না, সুতরাং লুপ শর্তের পছন্দটি সত্যই এটির ক্ষতি করে না ; এটি i
এক্সএমএম রেজিস্টারের উপাদানগুলিতে সমান্তরালে 4 টি মান চালিয়ে সিমডি সংখ্যার সংযোজন সহ স্বয়ংক্রিয়ভাবে ভেক্টরাইজ করে ।
# "naive" inner loop
.L3:
add eax, 1 # do {
paddd xmm0, xmm1 # vect_total_4.6, vect_vec_iv_.5
paddd xmm1, xmm2 # vect_vec_iv_.5, tmp114
cmp edx, eax # bnd.1, ivtmp.14 # bound and induction-variable tmp, I think.
ja .L3 #, # }while( n > i )
"finite" inner loop
# before the loop:
# xmm0 = 0 = totals
# xmm1 = {0,1,2,3} = i
# xmm2 = set1_epi32(4)
.L13: # do {
add eax, 1 # i++
paddd xmm0, xmm1 # total[0..3] += i[0..3]
paddd xmm1, xmm2 # i[0..3] += 4
cmp eax, edx
jne .L13 # }while( i != upper_limit );
then horizontal sum xmm0
and peeled cleanup for the last n%3 iterations, or something.
এটিতে একটি সরল স্কেলার লুপ রয়েছে যা আমি মনে করি এটি খুব ছোট n
এবং / অথবা অসীম লুপের ক্ষেত্রে ব্যবহার করে।
বিটিডাব্লু, এই উভয় লুপ লুপ ওভারহেডে একটি নির্দেশনা (এবং স্যান্ডিব্রিজে-পরিবার সিপিইউগুলিতে একটি উওফ) নষ্ট করে। sub eax,1
/ cmp / jcc এর jnz
পরিবর্তে add eax,1
আরও দক্ষ হবে। 2 এর পরিবর্তে 1 টি ইউওপ (সাব / জিসিসি বা সিএমপি / জিসিসির ম্যাক্রো-ফিউশন পরে)। উভয় লুপের পরে কোড EAX নিঃশর্তভাবে লিখেছে, সুতরাং এটি লুপের কাউন্টারটির চূড়ান্ত মান ব্যবহার করছে না।