पृष्ठभूमि की जानकारी: मैं कोड के दो टुकड़ों के लिए स्मृति आवश्यकताओं की तुलना करने की कोशिश कर रहा हूं जो कुछ संख्यात्मक गणना करते हैं। इसके लिए, मैं संकलित सी कोड के आकार की तुलना स्थिर रूप से जुड़े गणित पुस्तकालय के साथ कर रहा हूं।

हालांकि, मुझे कुछ अजीब परिणाम मिल रहे हैं जो इंगित करते हैं कि पूरी लाइब्रेरी को जोड़ा जा रहा है। मैं नीचे एक MWE का वर्णन कर रहा हूँ

// Program ex1.c
# include<math.h>
void main (void)
{
    float a = exp(2);

}

और

// Program ex2.c
# include <math.h>
void main(void)
{
    float a = exp(2);
    float b = pow(3,4);
    float c = sin(3.14159);
}

मैं फाइलों को निम्नानुसार संकलित करता हूं:

gcc -static -o ex1static.out ex1.c -lm
gcc -static -o ex2static.out ex2.c -lm

यदि प्रोग्राम 1 के लिए संकलित ऑब्जेक्ट में केवल exp() के लिए कोड होता है और प्रोग्राम 2 के लिए संकलित ऑब्जेक्ट के लिए exp(), pow() और sin() के लिए कोड होता है, तो दूसरा वाला पहले से बड़ा होगा। लेकिन दोनों वस्तुओं का आकार 912.6 kB समान है।

ऐसा क्यों हो रहा है और क्या यह सुनिश्चित करने का कोई तरीका है कि कोड के केवल आवश्यक हिस्से ही वस्तुओं में जुड़ जाएं?

3
yaska 29 जून 2018, 19:29

1 उत्तर

सबसे बढ़िया उत्तर

स्टेटिक लिब ऑब्जेक्ट फाइलों के आर्काइव हैं, और स्टैटिक लिब में लिंक करने से आर्काइव के केवल उन ऑब्जेक्ट फाइल सदस्यों को जोड़ा जाता है जो कम से कम एक अपरिभाषित संदर्भ को हल करते हैं।

यह सुनिश्चित करने के लिए कि केवल आवश्यक कोड जोड़ा जाता है, स्थिर lib को छोटी ऑब्जेक्ट फ़ाइलों से बना होना चाहिए, अधिमानतः प्रत्येक में एक निर्यातित वैश्विक के साथ।

इसके अलावा, आप एक समान प्रभाव प्राप्त कर सकते हैं यदि पुस्तकालय को -ffunction-sections/-fdata-sections के साथ संकलित किया जाता है और फिर आप लिंकर को --gc-sections पास करते हैं।

-ffunction-sections -fdata-sections दृष्टिकोण मूल रूप से एक-वैश्विक-प्रति-स्रोत दृष्टिकोण के बराबर है, लेकिन सीमाओं को स्थापित करने के लिए स्रोत फ़ाइलों का उपयोग करना अधिक लचीला है क्योंकि कभी-कभी चीजों को एक साथ समूहित करना वांछनीय हो सकता है (बड़ी अनुवाद इकाइयां अधिक कॉम्पैक्ट और अधिक हो सकती हैं अनुकूलित कोड)।

वैसे भी, आपके मामले में (lib आपके नियंत्रण में नहीं है), आप केवल -Wl,--gc-sections (-Wl जीसीसी उपसर्ग के विकल्प की कोशिश कर सकते हैं जो लिंकर को जीसीसी पास करना चाहिए) आपके उदाहरण और ग्लिब के साथ, मैं एक मूल 849KiB से लगभग 41KiB को बहा पाने में सक्षम था।

बहुत प्रभावशाली नहीं है, लेकिन ग्लिबैक वैसे भी स्थिर लिंकिंग को ध्यान में रखकर नहीं बनाया गया है। आप एक libc लाइब्रेरी के साथ बहुत बेहतर परिणाम प्राप्त कर सकते हैं, जैसे कि musl-libc

for ex in ex{1,2}.c; do for flg in '' -Wl,--gc-sections; do echo "$ex $flg"; musl-gcc -O0 $ex -static -lm $flg call.c && \ls -l a.out ; done ; done
ex1.c 
-rwxrwx--- 1 pjmp pjmp 8064 Jun 29 19:11 a.out
ex1.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 7744 Jun 29 19:11 a.out
ex2.c 
-rwxrwx--- 1 pjmp pjmp 8064 Jun 29 19:11 a.out
ex2.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 7744 Jun 29 19:11 a.out

अब यह बेहतर है, लेकिन आप सोच रहे होंगे कि उदाहरण 1 और 2 के लिए समान आकार क्यों हैं।

यदि आप -Wl,--print-map जोड़ते हैं, तो आप पाएंगे कि musl-libc से संबंधित ऑब्जेक्ट फ़ाइलें बिल्कुल भी शामिल नहीं की जा रही हैं किसी भी मामले में। कारण यह है कि, जीसीसी इन मानक कार्यों के बारे में जानता है और यह उत्पन्न फ़ंक्शन कॉल के बजाय ऑपकोड डालने से धोखा देता है। आप किसी अन्य अनुवाद इकाई द्वारा सुगम अप्रत्यक्ष की एक परत जोड़कर जीसीसी की धोखाधड़ी को कुछ हद तक हरा सकते हैं।

कॉल.सी:

double call1(double(*X)(double A), double A) { return X(A); }
double call2(double(*X)(double A,double B), double A, double B){ return X(A,B); }

Ex1.c

# include<math.h>
double call1(double(*X)(double A), double A);
double call2(double(*X)(double A,double B), double A, double B);
int main (void)
{
    float a = call1(exp,2);
}

Ex2.c

# include <math.h>
double call1(double(*X)(double A), double A);
double call2(double(*X)(double A,double B), double A, double B);
int main(void)
{
    float a = call1(exp,(2));
    float b = call2(pow,3,4);
    float c = call1(sin,(3.14159));
}

अब यह मुझे देता है:

Ex1.c 
-rwxrwx--- 1 pjmp pjmp 8216 Jun 29 19:15 a.out
Ex1.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 7984 Jun 29 19:15 a.out
Ex2.c 
-rwxrwx--- 1 pjmp pjmp 17088 Jun 29 19:15 a.out
Ex2.c -Wl,--gc-sections
-rwxrwx--- 1 pjmp pjmp 16856 Jun 29 19:15 a.out

- दो उदाहरणों के बीच एक ध्यान देने योग्य अंतर, जो संभव है धन्यवाद कि कैसे मसल बना है कई छोटे स्रोत/ऑब्जेक्ट फ़ाइलें ताकि और (या अधिक नहीं) प्रासंगिक संदर्भित कोड की तुलना में स्थिर रूप से लिंक करते समय जोड़ा जाता है।

3
PSkocik 29 जून 2018, 20:26