टीएल; डी doNotOptimizeAway
एक कृत्रिम "उपयोग" बनाता है।
यहां कुछ शब्दकोष: एक "def" ("परिभाषा") एक कथन है, जो एक चर के लिए मान निर्दिष्ट करता है; एक "उपयोग" एक कथन है, जो कुछ ऑपरेशन करने के लिए एक चर के मान का उपयोग करता है।
यदि किसी डीफ़ के तुरंत बाद बिंदु से, प्रोग्राम से बाहर निकलने के सभी पथों को एक चर के उपयोग का सामना नहीं करना पड़ता है, तो def को dead
कहा जाता है और मृत कोड उन्मूलन (डीसीई) पास इसे हटा देगा। जो बदले में अन्य डीफ मरने का कारण बन सकता है (यदि वह डीफ़ वैरिएबल ऑपरेटिंग के आधार पर उपयोग किया गया था), आदि
स्केलर प्रतिस्थापन (एसआरए) पास के स्केलर रिप्लेसमेंट के बाद कार्यक्रम की कल्पना करें, जो स्थानीय std::vector
को बदल देता है दो चर len
और ptr
। किसी बिंदु पर कार्यक्रम ptr
पर मान निर्दिष्ट करता है; वह कथन एक डीफ़ है।
अब, मूल कार्यक्रम ने वेक्टर के साथ कुछ भी नहीं किया; दूसरे शब्दों में len
या ptr
का उपयोग नहीं किया गया था। इसलिए, उनके सभी डीफ़ मर गए हैं और डीसीई उन्हें हटा सकता है, प्रभावी ढंग से सभी कोड हटा रहा है और बेंचमार्क बेकार बना सकता है।
doNotOptimizeAway(ptr)
जोड़ना एक कृत्रिम उपयोग बनाता है, जो डीसीई को डीफ़ को हटाने से रोकता है। (एक साइड नोट के रूप में, मुझे "+" में कोई बिंदु नहीं दिखता है, "जी" पर्याप्त होना चाहिए था)।
मेमोरी लोड और स्टोर्स के साथ तर्क की एक समान पंक्ति का पालन किया जा सकता है: एक स्टोर (डीएफ) मर जाता है अगर प्रोग्राम के अंत तक कोई रास्ता नहीं है, जिसमें उस स्टोर स्थान से लोड (उपयोग) होता है। जैसा कि मनमाना स्मृति स्थानों को ट्रैक करना व्यक्तिगत छद्म-रजिस्टर चरों को ट्रैक करने से बहुत कठिन है, कंपाइलर कारणों से रूढ़िवादी कारण है - यदि कार्यक्रम के अंत तक कोई रास्ता नहीं है, तो संभवतः उस स्टोर का उपयोग कर सकता है।
ऐसा एक मामला, स्मृति के क्षेत्र में एक स्टोर है, जिसे एलियास नहीं किया जाता है - उस स्मृति को समाप्त करने के बाद, संभवतः उस स्टोर का उपयोग नहीं हो सकता है, जो अपरिभाषित व्यवहार को ट्रिगर नहीं करता है। IOW, ऐसे कोई उपयोग नहीं हैं।
इस प्रकार एक कंपाइलर v.push_back(42)
को समाप्त कर सकता है। लेकिन escape
आता है - यह v.data()
को मनमाने ढंग से उपनाम के रूप में माना जाता है, जैसा उपरोक्त वर्णित @Leon है।
उदाहरण में clobber()
का उद्देश्य सभी एलियाज्ड मेमोरी का कृत्रिम उपयोग बनाना है। हमारे पास एक स्टोर है (push_back(42)
से), स्टोर एक ऐसे स्थान पर है जो विश्व स्तर पर अलियाकृत है (escape(v.data())
के कारण), इसलिए clobber()
संभावित रूप से उस स्टोर (आईओओ, स्टोर साइड इफेक्ट को देखने योग्य) का उपयोग कर सकता है, इसलिए कंपाइलर को स्टोर को हटाने की अनुमति नहीं है।
कुछ सरल उदाहरण:
उदाहरण मैं:
void f() {
int v[1];
v[0] = 42;
}
यह किसी भी कोड उत्पन्न नहीं करता है।
उदाहरण द्वितीय:
extern void g();
void f() {
int v[1];
v[0] = 42;
g();
}
यह g()
करने के लिए सिर्फ एक फोन, कोई स्मृति दुकान उत्पन्न करता है। फ़ंक्शन g
संभवतः v
तक नहीं पहुंच सकता है क्योंकि v
उपनाम नहीं है।
उदाहरण तृतीय:
void clobber() {
__asm__ __volatile__ ("" : : : "memory");
}
void f() {
int v[1];
v[0] = 42;
clobber();
}
पिछले उदाहरण में की तरह, कोई दुकान उत्पन्न क्योंकि v
एलियास नहीं है और clobber
करने के लिए कॉल करने के लिए कुछ भी नहीं inlined है।
उदाहरण चतुर्थ:
template<typename T>
void use(T &&t) {
__asm__ __volatile__ ("" :: "g" (t));
}
void f() {
int v[1];
use(v);
v[0] = 42;
}
इस बार v
पलायन (अर्थात संभवत: अन्य सक्रियण फ्रेम से पहुँचा जा सकता)। हालांकि, स्टोर अभी भी हटा दिया गया है, क्योंकि इसके बाद उस स्मृति का कोई संभावित उपयोग नहीं था (यूबी के बिना)।
उदाहरण वी:
template<typename T>
void use(T &&t) {
__asm__ __volatile__ ("" :: "g" (t));
}
extern void g();
void f() {
int v[1];
use(v);
v[0] = 42;
g(); // same with clobber()
}
और अंत में हम दुकान मिलता है, क्योंकि v
पलायन और संकलक परंपरागत ढंग से की ही होगी g
करने के लिए कॉल संग्रहीत मूल्य का उपयोग कर सकते हैं।
(प्रयोगों https://godbolt.org/g/rFviMI के लिए)
वास्तव में, "+ r" इसका मतलब है कि स्निपेट, दोनों पढ़ सकते हैं और गृहीत लिखेंगे। चूंकि (जीसीसी) कंपाइलर यह नहीं जानता कि एएसएम द्वारा डाटाम का उपयोग/संशोधित किया जा सकता है, यह किसी भी परिस्थिति में इसे अनुकूलित नहीं कर सकता है। –
वह कार्यक्षमता अब Google बेंचमार्क में बनाई गई है। बेंचमार्क :: DoNotOptimize जैसा कि यहां देखा गया है: github.com/google/benchmark – xaxxon