अधिकतम प्रदर्शन तरीका एएसएम में पूरे आंतरिक लूप को लिखने के लिए हो सकता है (call
निर्देशों सहित, अगर यह अनलॉक करने के लिए वास्तव में लायक है लेकिन इनलाइन नहीं है। निश्चित रूप से व्यवहार्य है अगर पूरी तरह से इनलाइनिंग बहुत अधिक यूओपी-कैश को कहीं और याद करती है)।
वैसे भी, सी ने आपके अनुकूलित लूप युक्त एएसएम फ़ंक्शन को कॉल किया है।तो आप अच्छी तरह से पूरे पाश खुद के अनुकूलन से आगे बाहर आ सकता है
Btw, सभी रजिस्टरों clobbering, यह मुश्किल जीसीसी एक बहुत अच्छा पाश बनाने के लिए बनाता है। (उदाहरण के लिए शायद एक रजिस्टर में एक पॉइंटर रखें, और स्मृति में एक अंत-सूचक, क्योंकि cmp mem,reg
अभी भी काफी कुशल है)।
एक asm
बयान है कि एक सरणी तत्व (Godbolt पर) को संशोधित करता है चारों ओर कोड जीसीसी/बजना चादर पर एक नज़र डालें:
void testloop(long *p, long count) {
for (long i = 0 ; i < count ; i++) {
asm(" # XXX asm operand in %0"
: "+r" (p[i])
:
: // "rax",
"rbx", "rcx", "rdx", "rdi", "rsi", "rbp",
"r8", "r9", "r10", "r11", "r12","r13","r14","r15"
);
}
}
#gcc7.2 -O3 -march=haswell
push registers and other function-intro stuff
lea rcx, [rdi+rsi*8] ; end-pointer
mov rax, rdi
mov QWORD PTR [rsp-8], rcx ; store the end-pointer
mov QWORD PTR [rsp-16], rdi ; and the start-pointer
.L6:
# rax holds the current-position pointer on loop entry
# also stored in [rsp-16]
mov rdx, QWORD PTR [rax]
mov rax, rdx # looks like a missed optimization vs. mov rax, [rax], because the asm clobbers rdx
XXX asm operand in rax
mov rbx, QWORD PTR [rsp-16] # reload the pointer
mov QWORD PTR [rbx], rax
mov rax, rbx # another weird missed-optimization (lea rax, [rbx+8])
add rax, 8
mov QWORD PTR [rsp-16], rax
cmp QWORD PTR [rsp-8], rax
jne .L6
# cleanup omitted.
बजना शून्य की ओर नीचे एक अलग काउंटर में गिना जाता है। लेकिन यह स्मृति-गंतव्य add [mem], -1
/jnz
के बजाय लोड/एड -1/स्टोर का उपयोग करता है।
यदि आप अपने हॉट लूप के उस भाग को संकलक में छोड़ने के बजाय स्वयं को लूप स्वयं लिखते हैं तो आप शायद इससे बेहतर कर सकते हैं।
पूर्णांक रजिस्ट्रारों पर रजिस्टर दबाव को कम करने के लिए पूर्णांक अंकगणितीय के लिए कुछ एक्सएमएम रजिस्टरों का उपयोग करने पर विचार करें, यदि संभव हो तो। इंटेल सीपीयू पर, जीपी और एक्सएमएम रजिस्टरों के बीच चलने से केवल 1 एएलयू यूओपी 1 सी विलंबता के साथ होता है। (यह अभी भी एएमडी पर 1 यूओपी है, लेकिन विशेष रूप से बुलडोजर-परिवार पर उच्च विलंबता)। एक्सएमएम रजिस्टरों में स्केलर पूर्णांक सामग्री करना बहुत खराब नहीं है, और यदि कुल यूओपी थ्रूपुट आपकी बाधा है, तो यह इसके लायक हो सकता है, या यह लागत से अधिक स्पिल/रीलोड बचाता है।
लेकिन निश्चित XMM के पाश काउंटर (paddd
/pcmpeq
/pmovmskb
/cmp
/jcc
या psubd
/ptest
/jcc
sub [mem], 1
/जेसीसी की तुलना में महान नहीं कर रहे हैं), या संकेत के लिए, या विस्तारित परिशुद्धता के लिए के लिए बहुत व्यवहार्य नहीं है अंकगणित (एक तुलना के साथ मैन्युअल रूप से कैर-आउट कर रहा है और paddq
के साथ कैर-इन 32-बिट मोड में भी बेकार है जहां 64-बिट पूर्णांक regs उपलब्ध नहीं हैं)। यदि आप लोड/स्टोर यूओएस पर बाधा नहीं डालते हैं, तो एक्सएमएम रजिस्टरों की बजाय स्मृति में स्पिल/पुनः लोड करना बेहतर होता है।
आप भी लूप (सफाई या कुछ और) बाहर से कार्य करने के लिए कॉल की जरूरत है, एक आवरण लिखने या add $-128, %rsp ; call ; sub $-128, %rsp
का उपयोग उन संस्करणों में लाल क्षेत्र को बचाने के लिये। (ध्यान दें कि -128
encodeable है के रूप में एक imm8
लेकिन +128
नहीं है।)
अपने सी समारोह में एक वास्तविक समारोह कॉल जरूरी यह लाल क्षेत्र ग्रहण करने के लिए सुरक्षित नहीं है सहित अप्रयुक्त है, हालांकि। (कंपाइलर-दृश्यमान) फ़ंक्शन कॉल के बीच कोई भी स्पिल/रीलोड लाल-ज़ोन का उपयोग कर सकता है, इसलिए asm
कथन में सभी रजिस्टरों को क्लॉबर करना उस व्यवहार को ट्रिगर करने की काफी संभावना है।
// a non-leaf function that still uses the red-zone with gcc
void bar(void) {
//cryptofunc(1); // gcc/clang don't use the redzone after this (not future-proof)
volatile int tmp = 1;
(void)tmp;
cryptofunc(1); // but gcc will use the redzone before a tailcall
}
# gcc7.2 -O3 output
mov edi, 1
mov DWORD PTR [rsp-12], 1
mov eax, DWORD PTR [rsp-12]
jmp cryptofunc(long)
आप संकलक विशिष्ट व्यवहार पर निर्भर करना चाहते हैं, तो आप गर्म पाश से पहले एक गैर इनलाइन समारोह (नियमित रूप से सी के साथ) कह सकते हैं। मौजूदा जीसीसी/क्लैंग के साथ, इससे उन्हें पर्याप्त स्टैक स्पेस आरक्षित कर दिया जाएगा क्योंकि उन्हें किसी भी तरह का स्टैक समायोजित करना होगा (से पहले rsp
को संरेखित करने के लिए)। यह भविष्य में सबूत नहीं है, लेकिन काम के साथ होना चाहिए।
GNU सी एक __attribute__((target("options")))
x86 function attribute है, लेकिन यह मनमाना विकल्प के लिए प्रयोग करने योग्य नहीं है, और -mno-redzone
हैं जिन्हें आप एक प्रति-समारोह के आधार पर बदल सकते हैं, या #pragma GCC target ("options")
एक संकलन इकाई के भीतर के साथ में से एक नहीं है।
आप
__attribute__((target("sse4.1,arch=core2")))
void penryn_version(void) {
...
}
नहीं बल्कि __attribute__((target("-mno-redzone")))
की तरह सामान का उपयोग कर सकते हैं।
#pragma GCC optimize
और optimize
फ़ंक्शन-एट्रिब्यूट (दोनों जिनमें से उत्पादन कोड के लिए इरादा नहीं है), लेकिन #pragma GCC optimize ("-mno-redzone")
वैसे भी काम नहीं करता है। मुझे लगता है कि विचार कुछ महत्वपूर्ण कार्यों को डीबग बिल्ड में -O2
के साथ अनुकूलित करने देना है। आप -f
विकल्प या -O
सेट कर सकते हैं।
बस एक अनचाहे हालांकि, लेकिन क्या आप सिर्फ एक अतिरिक्त डमी इनपुट निर्दिष्ट नहीं कर सकते हैं, जैसे कि जीसीसी इसे लाल क्षेत्र में रखता है और यह (हानिरहित रूप से) हो जाता है? –
एचएम। शायद भरोसेमंद नहीं। मैंने पाया है कि यह नियंत्रित करना बहुत कठिन है कि जीसीसी किस स्टैक पर, कब और कहां फैलता है। यह मैंने लिखा है कि अन्य क्रिप्टो सामान, मैंने जीसीसी की लिखने की प्रवृत्ति को दबाने के लिए मिश्रित सफलता के साथ प्रयास किया है, उदाहरण के लिए, छोटे कारणों से ढेर के लिए पूरी कुंजी टेबल। –
एक क्लॉबर के रूप में 'sp' जोड़ें? एक मेमोरी क्लॉबर जोड़ें? –