මම මෙම පිළිතුර ලියන විට, මම බැලුවේ <එදිරිව <= පොදුවේ මාතෘකාව පිළිබඳ ප්රශ්නය පමණි, නියත a < 901
එදිරිව එදිරිව නිශ්චිත උදාහරණය නොවේ a <= 900
. බොහෝ සම්පාදකයින් සෑම විටම <
හා අතර පරිවර්තනය කිරීමෙන් නියතයන්ගේ විශාලත්වය හැකිලී යයි<=
, උදා.
ARM සහ විශේෂයෙන් AArch64 සඳහා, ක්ෂණිකව කේතනය කිරීමට හැකිවීම රඳා පවතින්නේ වචනයක ඕනෑම ස්ථානයකට පටු ක්ෂේත්රයක් භ්රමණය කිරීමේ හැකියාව මත ය. එනිසා cmp w0, #0x00f000
සංකේතවත් කළ හැකියcmp w0, #0x00effff
නොවිය හැකි කළ හැකිය. එබැවින් සංසන්දනය කිරීම සඳහා වන කුඩා-රීතිය එදිරිව. සම්පාදක කාල නියතය සැමවිටම AArch64 සඳහා අදාළ නොවේ.
<එදිරිව <= පොදුවේ, ධාවන කාල විචල්ය තත්වයන් ඇතුළුව
බොහෝ යන්ත්රවල එකලස් කිරීමේ භාෂාවෙන්, සංසන්දනයක් සඳහා සංසන්දනයකට <=
සමාන පිරිවැයක් ඇත<
. මෙය ඔබ අතු බෙදී සිටියද, 0/1 නිඛිලයක් නිර්මාණය කිරීම සඳහා එය බූලියනීකරණය කරන්නේද, නැතහොත් ශාඛා රහිත තෝරාගත් මෙහෙයුමක් සඳහා (x86 CMOV වැනි) පුරෝකථනයක් ලෙස භාවිතා කරන්නේද යන්න මෙය අදාළ වේ. අනෙක් පිළිතුරු ආමන්ත්රණය කර ඇත්තේ ප්රශ්නයේ මෙම කොටස පමණි.
නමුත් මෙම ප්රශ්නය C ++ ක්රියාකරුවන් ගැන වේ, ප්රශස්තකරණයට ආදානය . සාමාන්යයෙන් ඔවුන් දෙදෙනාම සමානව කාර්යක්ෂම වේ; පොතේ උපදෙස් මුළුමනින්ම ව්යාජ එකක් ලෙස පෙනෙන්නේ සම්පාදකයින්ට සෑම විටම ඔවුන් විසින් ක්රියාත්මක කරන සංසන්දනය පරිවර්තනය කළ හැකි බැවිනි. නමුත් භාවිතා කිරීමේදී අවම වශයෙන් එක් ව්යතිරේකයක්වත් තිබේ<=
සම්පාදකයාට ප්රශස්තිකරණය කළ නොහැකි දෙයක් අහම්බෙන් නිර්මාණය කළ හැකි ඇත.
, චක්රයක් කොන්දේසියක් ලෙස, අවස්ථා තියෙනවා <=
වේ ගුණාත්මක වෙනස් <
එය චක්රයක් අනන්ත නොවන බව ඔප්පු සිට සම්පාදකවරයා නතර කරන විට,. මෙය ස්වයංක්රීය දෛශිකකරණය අක්රීය කරමින් විශාල වෙනසක් කළ හැකිය.
අත්සන් නොකළ පිටාර ගැලීම (යූබී) මෙන් නොව, පාදක -2 එතීම ලෙස හොඳින් අර්ථ දක්වා ඇත. අත්සන් කරන ලද පිටාර ගැලීම් යූබී මත පදනම්ව ප්රශස්තිකරණය කරන සම්පාදකයින් සමඟ අත්සන් කළ ලූප කවුන්ටර සාමාන්යයෙන් මෙයින් ආරක්ෂිත වේ: ++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++)
...
සම්පාදකයින්ට ප්රශස්තිකරණය කළ හැක්කේ නිර්වචනය නොකළ හැසිරීමට තුඩු දෙන ඒවා හැර, හැකි සියලුම ආදාන අගයන් සඳහා C ++ ප්රභවයේ (අර්ථ දක්වා ඇති සහ නීත්යානුකූලව නිරීක්ෂණය කළ හැකි) හැසිරීම ආරක්ෂා කරන ආකාරවලින් පමණි .
(සරල i <= size
ද ප්රශ්නයක් ඇති කරනු ඇත, නමුත් මම ඉහළ බැඳී ගණනය අහම්බෙන් ඔබ ගැන සැලකිලිමත් නැහැ නමුත් සම්පාදකවරයා සලකා යුතු ආදාන සඳහා අසීමිත පුඩුවක් හැකියාව හඳුන්වා පිළිබඳව වඩා තත්වික උදාහරණයක් බවයි. කල්පනා)
මෙම අවස්ථාවේ දී, size=0
මඟ පෙන්වන upper_bound=UINT_MAX
අතර i <= UINT_MAX
සෑම විටම සත්ය වේ. එබැවින් මෙම ලූපය අසීමිත වන අතර size=0
, සම්පාදකයාට ගරු කළ යුතුව ඇත්තේ ඔබ ක්රමලේඛකයා ලෙස කිසි විටෙකත් ප්රමාණය = 0 පසු කිරීමට අදහස් නොකරන බවයි. ප්රමාණය = 0 කළ නොහැකි බව ඔප්පු කළ හැකි සම්පාදකයාට මෙම ශ්රිතය ඇමතුම්කරුවකුට යොමු කළ හැකි නම්, නියමයි, එය හැකි තරම් ප්රශස්තිකරණය කළ හැකිය i < size
.
Asm like if(!size) skip the loop;
do{...}while(--size);
යනු for( i<size )
ලූපයක් ප්රශස්තිකරණය කිරීම සඳහා සාමාන්යයෙන් කාර්යක්ෂම ක්රමයකි, ලූපය තුළ සත්ය වටිනාකම i
අවශ්ය නොවේ නම් ( ලූප සෑම විටම "කරන්න ... අතර" විලාසිතාවට (වලිගය පැනීම) සම්පාදනය කරන්නේ ඇයි? ).
නමුත් එය}} අතර අසීමිත විය නොහැක: ඇතුලත් කළහොත් size==0
අපට 2 ^ n පුනරාවර්තන ලැබේ. ( C සඳහා අත්සන් නොකල පූර්ණ සංඛ්යා හරහා නැවත සැකසීම මඟින් ශුන්යය ඇතුළුව අත්සන් නොකල සියලු සංඛ්යා වලට වඩා ලූපයක් ප්රකාශ කිරීමට හැකි වේ, නමුත් එය රැගෙන යන ධජයක් නොමැතිව එය පහසු නොවේ.)
ලූප් කවුන්ටරය ආවරණය කිරීමේ හැකියාවක් ඇති හෙයින්, නවීන සම්පාදකයින් බොහෝ විට "අත්හරින්න" වන අතර ආක්රමණශීලී ලෙස ප්රශස්තිකරණය නොකරන්න.
උදාහරණය: 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;
}
ගොඩ්බෝල්ට් සම්පාදක ගවේෂකයේ x86-64 asm ක්ලැන්ග් 7.0 සහ gcc8.2
# 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
GCC සංවෘත ස්වරූපයක් දෙයාකාරයෙන්ම භාවිතා නොකරයි, එබැවින් ලූප් තත්ත්වය තේරීමෙන් එය සැබවින්ම හානියක් නොවේ ; එය එක්ස්එම්එම් ලේඛනයක 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
/ jnz
වෙනුවට add eax,1
/ cmp / jcc වඩා කාර්යක්ෂම වනු ඇත. 2 වෙනුවට 1 uop (උප / ජේසීසී හෝ සීඑම්පී / ජේසීසී සාර්ව විලයනයෙන් පසුව). ලූප දෙකටම පසුව ඇති කේතය කොන්දේසි විරහිතව EAX ලියයි, එබැවින් එය ලූප් කවුන්ටරයේ අවසාන අගය භාවිතා නොකරයි.