मैं वर्तमान में एक सी ++ टेम्पलेट अभिव्यक्ति लाइब्रेरी लिख रहा हूं और असेंबली स्तर पर हाथ से लिखित कोड के साथ कुछ तत्कालताओं की तुलना कर रहा हूं।अभिव्यक्ति टेम्पलेट बनाम हाथ से लिखित कोड
spinor multiply(vector const& a, vector const& b)
{
spinor result = {
a.at<1>() * b.at<1>() - a.at<2>() * b.at<2>()
- a.at<4>() * b.at<4>() - a.at<8>() * b.at<8>(),
a.at<1>() * b.at<2>() - a.at<2>() * b.at<1>(),
a.at<1>() * b.at<4>() - a.at<4>() * b.at<1>(),
a.at<1>() * b.at<8>() - a.at<8>() * b.at<1>(),
a.at<2>() * b.at<4>() - a.at<4>() * b.at<2>(),
a.at<2>() * b.at<8>() - a.at<8>() * b.at<2>(),
a.at<4>() * b.at<8>() - a.at<8>() * b.at<4>()
};
return result;
}
vector
वर्ग सिर्फ चार युगल पर एक आवरण जो at<index>()
सदस्य फ़ंक्शन का उपयोग करके पढ़ा जा सकता है है: हाथ से लिखा समारोह निम्नलिखित है। डिजाइन निर्णयों के कारण, चार घटकों के सूचकांक 1, 2, 4, 8
हैं जिन्हें सामान्य 0, 1, 2, 3
के बजाय at<index>()
के साथ उपयोग किया जाता है।
इस फ़ंक्शन का उद्देश्य दो वैक्टरों (मिंकोस्की-स्पेस में) के गुणा के परिणाम को वापस करना है। यदि आप जियोमेट्रिक बीजगणित से परिचित हैं, तो आपको result
का पहला घटक, a
और b
के आदान-प्रदान के तहत सममित है) और वेज-उत्पाद (शेष घटकों, एंटीसिमेट्रिक a
और b
के आदान-प्रदान के तहत)। यदि आप जियोमेट्रिक बीजगणित से परिचित नहीं हैं, तो इस कार्य को वैक्टरों को गुणा करने के लिए एक नुस्खे के रूप में लें।
अगर मैं जीसीसी 4.7 के साथ ऊपर समारोह संकलन और objdump -SC a.out
इस द्वारा दिए गए disassembly को देखो मेरा पीछा उत्पादन देता है:
400bc0: movsd 0x8(%rsi),%xmm6
400bc5: mov %rdi,%rax
400bc8: movsd (%rsi),%xmm8
400bcd: movsd 0x8(%rdx),%xmm5
400bd2: movapd %xmm6,%xmm9
400bd7: movsd (%rdx),%xmm7
400bdb: movapd %xmm8,%xmm0
400be0: mulsd %xmm5,%xmm9
400be5: movsd 0x10(%rsi),%xmm4
400bea: mulsd %xmm7,%xmm0
400bee: movsd 0x10(%rdx),%xmm1
400bf3: movsd 0x18(%rdx),%xmm3
400bf8: movsd 0x18(%rsi),%xmm2
400bfd: subsd %xmm9,%xmm0
400c02: movapd %xmm4,%xmm9
400c07: mulsd %xmm1,%xmm9
400c0c: subsd %xmm9,%xmm0
400c11: movapd %xmm3,%xmm9
400c16: mulsd %xmm2,%xmm9
400c1b: subsd %xmm9,%xmm0
400c20: movapd %xmm6,%xmm9
400c25: mulsd %xmm7,%xmm9
400c2a: movsd %xmm0,(%rdi)
400c2e: movapd %xmm5,%xmm0
400c32: mulsd %xmm8,%xmm0
400c37: subsd %xmm9,%xmm0
400c3c: movapd %xmm4,%xmm9
400c41: mulsd %xmm7,%xmm9
400c46: mulsd %xmm2,%xmm7
400c4a: movsd %xmm0,0x8(%rdi)
400c4f: movapd %xmm1,%xmm0
400c53: mulsd %xmm8,%xmm0
400c58: mulsd %xmm3,%xmm8
400c5d: subsd %xmm9,%xmm0
400c62: subsd %xmm7,%xmm8
400c67: movapd %xmm4,%xmm7
400c6b: mulsd %xmm5,%xmm7
400c6f: movsd %xmm0,0x10(%rdi)
400c74: mulsd %xmm2,%xmm5
400c78: movapd %xmm1,%xmm0
400c7c: mulsd %xmm6,%xmm0
400c80: movsd %xmm8,0x18(%rdi)
400c86: mulsd %xmm3,%xmm6
400c8a: mulsd %xmm2,%xmm1
400c8e: mulsd %xmm4,%xmm3
400c92: subsd %xmm7,%xmm0
400c96: subsd %xmm5,%xmm6
400c9a: subsd %xmm1,%xmm3
400c9e: movsd %xmm0,0x20(%rdi)
400ca3: movsd %xmm6,0x28(%rdi)
400ca8: movsd %xmm3,0x30(%rdi)
400cad: retq
400cae: nop
400caf: nop
यह मेरे लिए बहुत अच्छा लग रहा है - पहले के घटकों (%rsi
) और दूसरा (%rdx
) वैक्टर केवल एक बार उपयोग किए जाते हैं और वास्तविक गणना केवल रजिस्टरों में ही की जाती है। अंत में, परिणाम %rdi
रजिस्टर में पते पर लिखा गया है। चूंकि यह पहला तर्क रजिस्टर है, मुझे लगता है कि रिटर्न वैल्यू ऑप्टिमाइज़ेशन यहां नियोजित है।
ऊपर समारोह की अभिव्यक्ति टेम्पलेट संस्करण के लिए सूची निम्नलिखित के साथ इस की तुलना करें:
400cb0: mov (%rsi),%rdx
400cb3: mov 0x8(%rsi),%rax
400cb7: movsd 0x1f1(%rip),%xmm4 # 400eb0 <_IO_stdin_used+0x10>
400cbe:
400cbf: movsd 0x10(%rdx),%xmm3
400cc4: movsd 0x18(%rdx),%xmm0
400cc9: mulsd 0x10(%rax),%xmm3
400cce: xorpd %xmm4,%xmm0
400cd2: mulsd 0x18(%rax),%xmm0
400cd7: movsd 0x8(%rdx),%xmm2
400cdc: movsd (%rdx),%xmm1
400ce0: mulsd 0x8(%rax),%xmm2
400ce5: mulsd (%rax),%xmm1
400ce9: subsd %xmm3,%xmm0
400ced: subsd %xmm2,%xmm0
400cf1: addsd %xmm0,%xmm1
400cf5: movsd %xmm1,(%rdi)
400cf9: movsd (%rdx),%xmm0
400cfd: movsd 0x8(%rdx),%xmm1
400d02: mulsd 0x8(%rax),%xmm0
400d07: mulsd (%rax),%xmm1
400d0b: subsd %xmm1,%xmm0
400d0f: movsd %xmm0,0x8(%rdi)
400d14: movsd (%rdx),%xmm0
400d18: movsd 0x10(%rdx),%xmm1
400d1d: mulsd 0x10(%rax),%xmm0
400d22: mulsd (%rax),%xmm1
400d26: subsd %xmm1,%xmm0
400d2a: movsd %xmm0,0x10(%rdi)
400d2f: movsd 0x8(%rdx),%xmm0
400d34: movsd 0x10(%rdx),%xmm1
400d39: mulsd 0x10(%rax),%xmm0
400d3e: mulsd 0x8(%rax),%xmm1
400d43: subsd %xmm1,%xmm0
400d47: movsd %xmm0,0x18(%rdi)
400d4c: movsd (%rdx),%xmm0
400d50: movsd 0x18(%rdx),%xmm1
400d55: mulsd 0x18(%rax),%xmm0
400d5a: mulsd (%rax),%xmm1
400d5e: subsd %xmm1,%xmm0
400d62: movsd %xmm0,0x20(%rdi)
400d67: movsd 0x8(%rdx),%xmm0
400d6c: movsd 0x18(%rdx),%xmm1
400d71: mulsd 0x18(%rax),%xmm0
400d76: mulsd 0x8(%rax),%xmm1
400d7b: subsd %xmm1,%xmm0
400d7f: movsd %xmm0,0x28(%rdi)
400d84: movsd 0x10(%rdx),%xmm0
400d89: movsd 0x18(%rdx),%xmm1
400d8e: mulsd 0x18(%rax),%xmm0
400d93: mulsd 0x10(%rax),%xmm1
400d98: subsd %xmm1,%xmm0
400d9c: movsd %xmm0,0x30(%rdi)
400da1: retq
इस समारोह के हस्ताक्षर
spinor<product<vector, vector>>(product<vector, vector> const&)
है मुझे आशा है कि आप मुझ पर भरोसा है कि दोनों संस्करण एक ही देना परिणाम। पहली दो पंक्तियां पहले और दूसरे वेक्टर निकालने वाली हैं जिन्हें product
में संदर्भ के रूप में संग्रहीत किया जाता है। मैंने निम्नलिखित बातों के बारे में सोचा:
xorpd %xmm4,%xmm0
के साथ संयोजन मेंmovsd 0x1f1(%rip),%xmm4
क्या करता है? मुझे पहले ही पता चला है कि इसे "आरआईपी सापेक्ष एड्रेसिंग" कहा जाता है, http://www.x86-64.org/documentation/assembly.html- जीसीसी अधिक रजिस्टरों का उपयोग क्यों नहीं करता है, उदाहरण के लिए
0x10(%rax)
कैश करने के लिए जो चार बार पढ़ा जाता है?
मैं भी 100000000 यादृच्छिक वैक्टर पैदा करने और समय दोनों कार्यों के लिए आवश्यक लेने के द्वारा दोनों कार्यों बेंचमार्क:
ET: 7.5 sec
HW: 6.8 sec
हाथ से लिखा समारोह के बारे में 10% तेजी से होता है। क्या किसी को अभिव्यक्ति टेम्पलेट्स का अनुभव है और जानता है कि उन्हें अपने हाथ से लिखे गए समकक्ष के करीब कैसे करना है?
आपकी समस्या से कोई लेना देना नहीं, लेकिन यहां तक कि स्कॉइंग नियमों के साथ भी मैं कुछ मानक कंटेनर कक्षाओं के समान नए वर्गों को नामित करने की कोशिश नहीं करता हूं। यदि आप 'नेमस्पेस std का उपयोग कर' का उपयोग करते हैं, तो चीजें बहुत भ्रमित हो सकती हैं। –
@ यह वास्तविक ईटी कोड के बिना कठिन होने जा रहा है। साझा करने के लिए परवाह? – sehe
@ जोचिमपिलबोर्ग: यदि आप 'नेमस्पेस std का उपयोग करते हुए' का उपयोग करते हैं, तो मुझे कोई परवाह नहीं है कि आपको समस्याएं मिलती हैं (आपको अधिक विशिष्ट होना चाहिए) ... लेकिन मैं नाम के संघर्ष से बचने की कोशिश करने के बारे में सामान्य विचार से सहमत हूं। –