मैं एक ऐसे कन्स्ट्रक्टर को अनुकूलित कर रहा हूं जिसे हमारे ऐप के सबसे निचले लूप में से एक में बुलाया जाता है। प्रश्न में कक्षा लगभग 100 बाइट चौड़ी है, जिसमें int
एस, float
एस, bool
एस, और तुच्छ संरचनाएं शामिल हैं, और इसे तुलनीय रूप से कॉपी करने योग्य होना चाहिए (इसमें एक नॉनट्रिविअल डिफॉल्ट कन्स्ट्रक्टर है, लेकिन कोई विनाशक या वर्चुअल फ़ंक्शन नहीं है)। यह अक्सर इतना बनाया जाता है कि प्रत्येक नैनोसेकंद इस सीटीआर में बिताए गए समय के अतिरिक्त सर्वर हार्डवेयर के लगभग $ 6,000 तक काम करता है जिसे हमें खरीदने की ज़रूरत है।क्या एमसीसी को स्मृति-संरेखित वस्तुओं के लिए कुशल रचनाकार उत्पन्न करने के लिए मजबूर किया जा सकता है?
हालांकि, मुझे लगता है कि जीसीसी इस कन्स्ट्रक्टर के लिए बहुत ही कुशल कोड उत्सर्जित नहीं कर रहा है (यहां तक कि -O3 -march
आदि सेट के साथ)। जीसीसी के कन्स्ट्रक्टर के कार्यान्वयन, प्रारंभिक सूची के माध्यम से डिफ़ॉल्ट मानों को भरना, चलाने के लिए लगभग 34ns लगता है। यदि इस डिफ़ॉल्ट कन्स्ट्रक्टर के बजाय मैं एक हाथ से लिखित फ़ंक्शन का उपयोग करता हूं जो कि ऑब्जेक्ट की मेमोरी स्पेस को सीधे सिमड इंट्रिनिक्स और पॉइंटर गणित के साथ लिखता है, तो निर्माण में लगभग 8ns लगते हैं।
क्या मैं __attribute__
सिम सीमाओं पर स्मृति-गठबंधन होने के लिए ऐसी वस्तुओं के लिए एक कुशल निर्माता को उत्सर्जित करने के लिए जीसीसी प्राप्त कर सकता हूं? या क्या मुझे पुराने स्कूल की तकनीकों का सहारा लेना चाहिए जैसे असेंबली में अपना खुद का मेमोरी प्रारंभकर्ता लिखना?
यह ऑब्जेक्ट केवल स्टैक पर स्थानीय के रूप में बनाया गया है, इसलिए कोई भी नया/मॉलोक ओवरहेड लागू नहीं होता है।
प्रसंग:
इस वर्ग के एक स्थानीय चर के रूप में ढेर पर यह निर्माण, चुनिंदा गैर मूलभूत मूल्यों के साथ कुछ क्षेत्रों लेखन, और फिर इसे एक समारोह के लिए गुजर (संदर्भ द्वारा) द्वारा किया जाता है, जो इसके संदर्भ को दूसरे के पास भेजता है।
struct Trivial {
float x,y,z;
Trivial() : x(0), y(0), z(0) {};
};
struct Frobozz
{
int na,nb,nc,nd;
bool ba,bb,bc;
char ca,cb,cc;
float fa,fb;
Trivial va, vb; // in the real class there's several different kinds of these
// and so on
Frobozz() : na(0), nb(1), nc(-1), nd(0),
ba(false), bb(true), bc(false),
ca('a'), cb('b'), cc('c'),
fa(-1), fb(1.0) // etc
{}
} __attribute__((aligned(16)));
// a pointer to a func that takes the struct by reference
typedef int (*FrobozzSink_t)(Frobozz&);
// example of how a function might construct one of the param objects and send it
// to a sink. Imagine this is one of thousands of event sources:
int OversimplifiedExample(int a, float b)
{
Frobozz params;
params.na = a; params.fb = b; // other fields use their default values
FrobozzSink_t funcptr = AssumeAConstantTimeOperationHere();
return (*funcptr)(params);
}
यहाँ इष्टतम निर्माता हाल में निर्माण उदाहरण में एक स्थिर "टेम्पलेट" उदाहरण से कॉपी करके, आदर्श रूप में एक समय में 16 बाइट्स काम करने के लिए SIMD ऑपरेटरों का उपयोग करके काम करेगा। इसके बजाय GCC OversimplifiedExample() — स्ट्रक्चर बाइट-बाय-बाइट को भरने के लिए तत्काल mov ops की एक श्रृंखला के लिए बिल्कुल गलत काम करता है।
// from objdump -dS
int OversimplifiedExample(int a, float b)
{
a42:55 push %ebp
a43:89 e5 mov %esp,%ebp
a45:53 push %ebx
a46:e8 00 00 00 00 call a4b <_Z21OversimplifiedExampleif+0xb>
a4b:5b pop %ebx
a4c:81 c3 03 00 00 00 add $0x3,%ebx
a52:83 ec 54 sub $0x54,%esp
// calling the 'Trivial()' constructors which move zero, word by word...
a55:89 45 e0 mov %eax,-0x20(%ebp)
a58:89 45 e4 mov %eax,-0x1c(%ebp)
a5b:89 45 e8 mov %eax,-0x18(%ebp)
a5e:89 45 ec mov %eax,-0x14(%ebp)
a61:89 45 f0 mov %eax,-0x10(%ebp)
a64:89 45 f4 mov %eax,-0xc(%ebp)
// filling out na/nb/nc/nd..
a67:c7 45 c4 01 00 00 00 movl $0x1,-0x3c(%ebp)
a71:c7 45 c8 ff ff ff ff movl $0xffffffff,-0x38(%ebp)
a78:89 45 c0 mov %eax,-0x40(%ebp)
a7b:c7 45 cc 00 00 00 00 movl $0x0,-0x34(%ebp)
a82:8b 45 0c mov 0xc(%ebp),%eax
// doing the bools and chars by moving one immediate byte at a time!
a85:c6 45 d0 00 movb $0x0,-0x30(%ebp)
a89:c6 45 d1 01 movb $0x1,-0x2f(%ebp)
a8d:c6 45 d2 00 movb $0x0,-0x2e(%ebp)
a91:c6 45 d3 61 movb $0x61,-0x2d(%ebp)
a95:c6 45 d4 62 movb $0x62,-0x2c(%ebp)
a99:c6 45 d5 63 movb $0x63,-0x2b(%ebp)
// now the floats...
a9d:c7 45 d8 00 00 80 bf movl $0xbf800000,-0x28(%ebp)
aa4:89 45 dc mov %eax,-0x24(%ebp)
// FrobozzSink_t funcptr = GetFrobozz();
aa7:e8 fc ff ff ff call aa8 <_Z21OversimplifiedExampleif+0x68>
// return (*funcptr)(params);
aac:8d 55 c0 lea -0x40(%ebp),%edx
aaf:89 14 24 mov %edx,(%esp)
ab2:ff d0 call *%eax
ab4:83 c4 54 add $0x54,%esp
ab7:5b pop %ebx
ab8:c9 leave
ab9:c3 ret
}
मैं इस वस्तु की एक 'डिफ़ॉल्ट टेम्पलेट' के निर्माण के लिए, और फिर इसे डिफ़ॉल्ट निर्माता में थोक-कॉपी, एक छिपे हुए 'डमी' निर्माता कि साथ प्रवंचना का एक सा करके जीसीसी के लिए प्रोत्साहित करने की कोशिश की आधार आदर्श और फिर डिफ़ॉल्ट होने बस इसे कॉपी:
struct Frobozz
{
int na,nb,nc,nd;
bool ba,bb,bc;
char ca,cb,cc;
float fa,fb;
Trivial va, vb;
inline Frobozz();
private:
// and so on
inline Frobozz(int dummy) : na(0), /* etc etc */ {}
} __attribute__((aligned(16)));
Frobozz::Frobozz()
{
const static Frobozz DefaultExemplar(69105);
// analogous to copy-on-write idiom
*this = DefaultExemplar;
// or:
// memcpy(this, &DefaultExemplar, sizeof(Frobozz));
}
लेकिन यह कुछ बेमानी ढेर नकल की वजह से उत्पन्न प्रारंभकर्ता सूची के साथ बुनियादी डिफ़ॉल्ट से भी धीमी कोड।
अंत में मैं, *this = DefaultExemplar
कदम करने के लिए एक inlined मुक्त समारोह लिख pipelinedMOVDQA SSE2 opcodes कि कुशलतापूर्वक struct नकल जारी करने के लिए स्मृति संरेखण के बारे में संकलक intrinsics और मान्यताओं का उपयोग कर का सहारा लिया। यह मुझे प्रदर्शन की जरूरत है, लेकिन यह icky है। मैंने सोचा कि असेंबली में शुरुआती लिखने के मेरे दिन मेरे पीछे थे, और मैं वास्तव में सिर्फ जीसीसी के अनुकूलक को सही जगह पर सही कोड छोड़ देता हूं।
क्या कोई तरीका है कि मैं अपने कन्स्ट्रक्टर के लिए इष्टतम कोड उत्पन्न करने के लिए जीसीसी प्राप्त कर सकता हूं, कुछ कंपाइलर सेटिंग या अतिरिक्त __attribute__
मुझे याद आया है?
यह उबंटू पर चल रहा जीसीसी 4.4 है।कंपाइलर झंडे में -m32 -march=core2 -O3 -fno-strict-aliasing -fPIC
(दूसरों के बीच) शामिल हैं। पोर्टेबिलिटी पर विचार नहीं है, और मैं यहां प्रदर्शन के मानकों-अनुपालन को बलिदान देने के लिए पूरी तरह से तैयार हूं।
समय सीधे rdtsc
, जैसे की एन OversimplifiedExample एक पाश() टाइमर संकल्प की वजह से ध्यान और कैश और सांख्यिकीय महत्व और इतने पर के साथ नमूने के बीच कॉल को मापने के साथ समय स्टाम्प काउंटर को पढ़ कर प्रदर्शन किया गया।
मैंने कॉल साइटों की संख्या को जितना संभव हो सके कम करके इसे अनुकूलित किया है, लेकिन मैं अभी भी जानना चाहता हूं कि जीसीसी से बेहतर सीटीआर कैसे प्राप्त करें।
क्या आपने हाल ही में जीसीसी की कोशिश की है, जैसे कि 4.6.2 (या जल्द ही रिलीज होने वाला नवीनतम स्नैपशॉट)? –
क्या आप निर्माता की परिभाषा को छोड़ सकते हैं और इसे पूरी तरह से हाथ में लिख सकते हैं? जोखिम भरा और बनाए रखना मुश्किल है, लेकिन 34 * $ 6000 के लिए यह खुद के लिए भुगतान करेगा मुझे संदेह है कि – Flexo
क्या आपने विभिन्न '-msse' झंडे को जोड़ने का भी प्रयास किया है? मुझे लगता है कि कुछ मामलों में उन्हें एसएसई के लिए जरूरी है। इसके अलावा मेरा सुझाव है कि आप हाल ही में एक जीसीसी प्राप्त करें और इसके मैनपेज को ब्राउज़ करें, इस बारे में सोचें कि क्या हर विकल्प आपकी स्थिति में सुधार कर सकता है और फिर इसे आज़माएं। – PlasmaHH