C में वास्तव में रजिस्टर कीवर्ड कब उपयोगी है?


10

मैं registerसी में कीवर्ड के उपयोग के बारे में उलझन में हूं । आमतौर पर बताया जाता है कि स्टैकओवरफ्लो पर इस प्रश्न में इसके उपयोग की आवश्यकता नहीं है ।

क्या यह कीवर्ड आधुनिक संकलक के कारण C में पूरी तरह से निरर्थक है या क्या ऐसी स्थितियाँ हैं जिनमें यह अभी भी उपयोगी हो सकता है? यदि हाँ, तो ऐसी कौन सी स्थितियाँ हैं जिनमें registerखोजशब्द का उपयोग वास्तव में सहायक है?


4
मुझे लगता है कि जुड़ा हुआ प्रश्न और उसके उत्तर वही हैं जो आप यहाँ उम्मीद कर सकते हैं। इसलिए कोई नई जानकारी नहीं होगी जो आपको यहां मिल सके।
उवे प्लोनस

@ यूवेप्लोनस मैंने constकीवर्ड के बारे में ऐसा ही सोचा था लेकिन इस सवाल ने साबित कर दिया कि मैं गलत था। इसलिए मैं इंतजार करूंगा और देखूंगा कि मुझे क्या मिलेगा।
असीम बंसल

मुझे लगता है कि constकीवर्ड रजिस्टर के खिलाफ कुछ अलग है।
उवे प्लोनस

4
यह उपयोगी है यदि आप गलती से समय पर वापस जाते हैं और शुरुआती सी कंपाइलरों में से एक का उपयोग करने के लिए मजबूर हैं। इसके अलावा यह बिल्कुल भी उपयोगी नहीं है, यह वर्षों से पूरी तरह से अप्रचलित है।
जॉन बी जुएल

@ यूवेप्लोनस मेरा मतलब सिर्फ यह था कि मेरे लिए अनजान परिदृश्य हो सकते हैं जिसमें कोई कीवर्ड उपयोगी हो सकता है।
असीम बंसल

जवाबों:


11

यह भाषा के संदर्भ में बेमानी नहीं है, यह सिर्फ इतना है कि इसका उपयोग करके, आप संकलक को बता रहे हैं, आप रजिस्टर में एक चर संग्रहित करना "पसंद" करेंगे। हालांकि पूरी तरह से शून्य गारंटी है कि यह वास्तव में रनटाइम के दौरान होगा।


9
इससे अधिक, यह लगभग हमेशा ऐसा होता है कि कंपाइलर को सबसे अच्छा पता होता है और आप अपनी सांस को बर्बाद कर रहे हैं
डैनियल ग्रैज़र

6
@jozefg: और भी बुरा। आप वह जोखिम चलाते हैं जो संकलक आपके अनुरोध / संकेत का सम्मान करता है और परिणामस्वरूप बदतर कोड उत्पन्न करता है ।
बार्ट वैन इनगेन शेनॉ

9

जैसा कि पहले ही उल्लेख किया गया है, कंपाइलर ऑप्टिमाइज़र अनिवार्य रूप registerसे अलियासिंग को रोकने के अलावा अन्य उद्देश्यों के लिए अप्रचलित कीवर्ड को प्रस्तुत करते हैं । हालांकि, वहाँ पूरे codebases जो संकलित किए जाते हैं के साथ अनुकूलन बंद (हैं -O0में जीसीसी बात )। ऐसे कोड के लिए, registerकीवर्ड का बहुत प्रभाव हो सकता है। विशेष रूप से, चर जिन्हें अन्यथा स्टैक पर एक स्लॉट मिलेगा (अर्थात सभी फ़ंक्शन पैरामीटर और स्वचालित चर) को कीवर्ड के साथ घोषित किए जाने पर सीधे रजिस्टर में रखा जा सकता है register

यहां एक वास्तविक दुनिया उदाहरण है: मान लें कि कुछ डेटाबेस पुनर्प्राप्ति हुई है और पुनर्प्राप्ति कोड ने पुनर्प्राप्त किए गए टपल को सी संरचना में भर दिया है। इसके अलावा, मान लें कि इस सी संरचना के कुछ सबसेट को किसी अन्य संरचना में कॉपी करने की आवश्यकता है - शायद यह दूसरी संरचना एक कैश रिकॉर्ड है जो डेटाबेस में संग्रहीत मेटाडेटा का प्रतिनिधित्व करता है, जो कि स्मृति की कमी के कारण संग्रहीत किए गए प्रत्येक मेटाडेटा रिकॉर्ड के सबसेट को कैश करता है डेटाबेस में।

एक फ़ंक्शन को देखते हुए जो प्रत्येक संरचना प्रकार के लिए एक पॉइंटर लेता है और जिसका एकमात्र काम यह है कि प्रारंभिक संरचना से कुछ सदस्यों को दूसरी संरचना में कॉपी करें: स्टैक पर लाइव पॉइंटर पॉवर्स चर रहेंगे। चूंकि असाइनमेंट एक संरचना के सदस्यों से दूसरे में होते हैं, इसलिए संरचना के पते, प्रत्येक असाइनमेंट के लिए, एक रजिस्टर में लोड किए जाते हैं ताकि कॉपी किए गए संरचना के सदस्यों की पहुंच का प्रदर्शन किया जा सके। यदि registerकीवर्ड के साथ स्ट्रक्चर पॉइंटर्स घोषित किए जाने थे , तो प्रत्येक असाइनमेंट के लोड-एड्रेस-इन-रजिस्टर निर्देशों को प्रभावी ढंग से काटते हुए, स्ट्रक्चर के पते रजिस्टरों में रहेंगे।

फिर, याद रखें कि वर्णन ऊपर पर लागू होता है unoptimized कोड।


6

आप मूल रूप से संकलक को बताते हैं कि आप चर का पता नहीं लेंगे और संकलक तब और अधिक अनुकूलन कर सकते हैं। जहां तक ​​मुझे पता है, आधुनिक कंपाइलर यह निर्धारित करने में काफी सक्षम हैं कि एक चर को रजिस्टर में रखा जाना चाहिए या नहीं।

उदाहरण:

int main(){
        int* ptr;
        int a;
        register int b;
        ptr = &a;
        ptr = &b; //this won't compile
        return 0;
} 

का पता लगाने या लेने का?
detly

@ निश्चित रूप से: आप निश्चित रूप से सही हैं
लुकास

0

16-बिट कंप्यूटर दिनों में, एक को अक्सर 32-बिट गुणन और विभाजन को निष्पादित करने के लिए कई रजिस्टरों की आवश्यकता होती है। चूंकि फ़्लोटिंग पॉइंट यूनिट्स को चिप्स में शामिल किया गया था और फिर 64-बिट आर्किटेक्चर 'पर अधिकार' कर लिया गया था, रजिस्टरों की चौड़ाई और उनमें से दोनों की संख्या में विस्तार हुआ। यह अंततः सीपीयू के पूर्ण पुन: आर्किटेक्चर की ओर जाता है। विकिपीडिया पर रजिस्टर फाइलें देखें ।

संक्षेप में, यह पता लगाने में आपको थोड़ा समय लगेगा कि वास्तव में क्या हो रहा है यदि आप 64-बिट X86 या ARM चिप पर हैं। यदि आप 16-बिट एम्बेडेड CPU पर हैं, तो यह वास्तव में आपको कुछ प्राप्त कर सकता है। हालांकि, अधिकांश छोटे एम्बेडेड चिप्स कुछ भी महत्वपूर्ण समय पर नहीं चलते हैं - आपका माइक्रोवेव ओवन आपके टचपैड को 10,000 गुना दूसरे - कुछ भी नहीं है कि 4Mhz सीपीयू उपभेद कर सकता है।


1
4 MIPS / 10,000 चुनाव / सेकंड = 400 निर्देश / सर्वेक्षण। यह लगभग उतना मार्जिन नहीं है जितना आप करना चाहते हैं। यह भी ध्यान दें कि काफी कुछ 4 मेगाहर्ट्ज प्रोसेसर आंतरिक रूप से माइक्रोकोडेड थे, जिसका अर्थ है कि वे 1 एमआईपी / मेगाहर्ट्ज के पास कहीं नहीं थे।
जॉन आर। स्ट्रॉहेम जूल

@ JohnR.Strohm - ऐसी स्थितियाँ हो सकती हैं, जिनमें से कोई यह अनुमान लगाने का औचित्य सिद्ध कर सकता है कि इसे लेने के लिए कितने निर्देश चक्र हैं, लेकिन अक्सर सस्ता तरीका अब एक तेज़ चिप प्राप्त करना है और उत्पाद को दरवाजे से बाहर निकालना है। निश्चित रूप से दिए गए उदाहरण में, किसी को 10,000 पर नमूना जारी रखने की आवश्यकता नहीं है यदि किसी के पास एक आदेश है - यह बिना किसी नुकसान के एक सेकंड के एक चौथाई के लिए नमूना फिर से शुरू नहीं कर सकता है। यह पता लगाना कठिन होता जा रहा है कि प्रोग्रामर द्वारा निर्देशित ऑप्टिमाइज़ेशन क्या मायने रखता है।
मेरेडिथ पुअर

1
हमेशा "बस एक तेज़ चिप प्राप्त करना और उत्पाद को दरवाजे से बाहर निकालना" संभव नहीं है। वास्तविक समय छवि प्रसंस्करण पर विचार करें। 640x480 पिक्सेल / फ्रेम x 60 फ़्रेम / सेकंड x एन निर्देश प्रति पिक्सेल जल्दी जोड़ता है। (रियल-टाइम इमेज प्रोसेसिंग से सबक यह है कि आप अपने पिक्सेल कर्नेल पर खून पसीना बहाते हैं और आप बस बाकी सब को अनदेखा करते हैं, क्योंकि यह प्रति पंक्ति एक बार या प्रति पैच या एक बार फ्रेम के अनुसार चलता है, प्रति पंक्ति सैकड़ों बार विरोध किया जाता है या पैच या दसियों या प्रति फ्रेम हजारों बार।)
जॉन आर। स्ट्रॉह्म

@ JohnR.Strohm - वास्तविक समय छवि प्रसंस्करण उदाहरण लेते हुए, मैं न्यूनतम वातावरण 32 बिट्स मानूंगा। एक अंग पर बाहर जाना (क्योंकि मुझे नहीं पता कि यह कितना व्यावहारिक है) चिप्स में निर्मित कई ग्राफिक्स त्वरक छवि मान्यता के लिए भी उपयोग करने योग्य हो सकते हैं, इसलिए एआरएम चिप्स (उदाहरण के लिए) जिसमें एकीकृत रेंडरिंग इंजन हो सकते हैं, अतिरिक्त ALUs प्रयोग करने योग्य हो सकते हैं मान्यता के लिए। उस समय तक अनुकूलन के लिए 'रजिस्टर' कीवर्ड का उपयोग समस्या का एक छोटा हिस्सा है।
मेरेडिथ गरीब

-3

यह सुनिश्चित करने के लिए कि रजिस्टर कीवर्ड का कोई महत्व है, छोटे उदाहरण कोड नहीं करेंगे। यहां एक सी-कोड है जो मुझे सुझाव देता है, रजिस्टर कीवर्ड का अभी भी एक महत्व है। लेकिन यह लिनक्स पर जीसीसी के साथ भिन्न हो सकता है, मुझे नहीं पता। क्या एक सीपीयू रजिस्टर में संग्रहित किया जाएगा या नहीं? लिनक्स उपयोगकर्ताओं (विशेष रूप से) को जीसीसी और अनुकूलन के साथ संकलन करना चाहिए। Borland bcc32 के साथ रजिस्टर कीवर्ड (इस उदाहरण में) कार्य करता है, क्योंकि & -पॉपरर, रजिस्टर पूर्णांक के लिए त्रुटि कोड देता है। ध्यान दें! यह विंडोज पर बोरलैंड के साथ एक छोटे से उदाहरण के साथ मामला नहीं है! वास्तव में यह देखने के लिए कि संकलक क्या अनुकूलन करता है या नहीं, यह एक छोटे उदाहरण से अधिक होना है। खाली छोरों नहीं करेंगे! फिर भी - यदि कोई पता & -ऑपरेटर के साथ पढ़ा जा सकता है, तो चर को सीपीयू रजिस्टर में संग्रहीत नहीं किया जाता है। लेकिन अगर एक पंजीकृत घोषित चर नहीं पढ़ा जा सकता है (संकलन में त्रुटि कोड के कारण) - मुझे यह मान लेना होगा कि रजिस्टर कीवर्ड वास्तव में चर को सीपीयू-रजिस्टर में डालता है। यह विभिन्न प्लेटफार्मों पर भिन्न हो सकता है, मुझे नहीं पता। (यदि यह काम करता है, तो "टिक" की संख्या रजिस्टर घोषणा के साथ बहुत कम होगी।

/* reg_or_not.c */  

#include <stdio.h>
#include <time.h>
#include <stdlib> //not requiered for Linux
#define LAPSb 50
#define LAPS 50000
#define MAXb 50
#define MAX 50000


int main (void)
{
/* 20 ints and 2 register ints */   

register int k,l;
int a,aa,b,bb,c,cc,d,dd,e,ee,f,ff,g,gg,h,hh,i,ii,j,jj;


/* measure some ticks also */  

clock_t start_1,start_2; 
clock_t finish_1,finish_2;
long tmp; //just for the workload 


/* pointer declarations of all ints */

int *ap, *aap, *bp, *bbp, *cp, *ccp, *dp, *ddp, *ep, *eep;
int *fp, *ffp, *gp, *ggp, *hp, *hhp, *ip, *iip, *jp, *jjp;
int *kp,*lp;

/* end of declarations */
/* read memory addresses, if possible - which can't be done in a CPU-register */     

ap=&a; aap=&aa; bp=&b; bbp=&bb;
cp=&c; ccp=&cc; dp=&d; ddp=&dd;
ep=&e; eep=&ee; fp=&f; ffp=&ff;
gp=&g; ggp=&gg; hp=&h; hhp=&hh;
ip=&i; iip=&ii; jp=&j; jjp=&jj;

//kp=&k;  //won't compile if k is stored in a CPU register  
//lp=&l;  //same - but try both ways !


/* what address , isn't the issue in this case - but if stored in memory    some "crazy" number will be shown, whilst CPU-registers can't be read */

printf("Address a aa: %u     %u\n",a,aa);
printf("Address b bb: %u     %u\n",b,bb);
printf("Address c cc: %u     %u\n",c,cc);
printf("Address d dd: %u     %u\n",d,dd);
printf("Address e ee: %u     %u\n",e,ee);
printf("Address f ff: %u     %u\n",f,ff);
printf("Address g gg: %u     %u\n",g,gg);
printf("Address h hh: %u     %u\n",h,hh);
printf("Address i ii: %u     %u\n",i,ii);
printf("Address j jj: %u     %u\n\n",j,jj);

//printf("Address k:  %u \n",k); //no reason to try "k" actually is in a CPU-register 
//printf("Address l:  %u \n",l); 


start_2=clock(); //just for fun      

/* to ensure workload */
for (a=1;a<LAPSb;a++) {for (aa=0;aa<MAXb;aa++);{tmp+=aa/a;}}
for (b=1;b<LAPSb;b++) {for (bb=0;bb<MAXb;bb++);{tmp+=aa/a;}}
for (a=1;c<LAPSb;c++) {for (cc=0;cc<MAXb;cc++);{tmp+=bb/b;}}
for (d=1;d<LAPSb;d++) {for (dd=0;dd<MAXb;dd++);{tmp+=cc/c;}}
for (e=1;e<LAPSb;e++) {for (ee=0;ee<MAXb;ee++);{tmp+=dd/d;}}
for (f=1;f<LAPSb;f++) {for (ff=0;ff<MAXb;ff++);{tmp+=ee/e;}}
for (g=1;g<LAPSb;g++) {for (gg=0;gg<MAXb;gg++);{tmp+=ff/f;}}
for (h=1;h<LAPSb;h++) {for (hh=0;hh<MAXb;hh++);{tmp+=hh/h;}}
for (jj=1;jj<LAPSb;jj++) {for (ii=0;ii<MAXb;ii++);{tmp+=ii/jj;}}

start_1=clock(); //see following printf
for (i=0;i<LAPS;i++) {for (j=0;j<MAX;j++);{tmp+=j/i;}} /* same double   loop - in supposed memory */
finish_1=clock(); //see following printf

printf ("Memory: %ld ticks\n\n", finish_1 - start_1); //ticks for memory

start_1=clock(); //see following printf
for (k=0;k<LAPS;k++) {for (l=0;l<MAX;l++);{tmp+=l/k;}}  /* same double       loop - in supposed register*/
finish_1=clock(); //see following printf     

printf ("Register: %ld ticks\n\n", finish_1 - start_1); //ticks for CPU register (?) any difference ?   

finish_2=clock();

printf ("Total: %ld ticks\n\n", finish_2 - start_2); //really for fun only           

system("PAUSE"); //only requiered for Windows, so the CMD-window doesn't vanish     

return 0;

} 

ऊपर शून्य के साथ एक विभाजन होगा, कृपया {tmp + = ii / jj;} को {tmp + =j / ii;} में बदलें - इसके लिए वास्तव में क्षमा करें
जॉन पी एरिक्सन

इसके अलावा k और i को 1 से शुरू करें - शून्य नहीं। बहुत खेद है।
जॉन पी एरिकसन

3
आप टिप्पणियों में सुधार लिखने के बजाय अपने उत्तर को संपादित कर सकते हैं।
जनवरी डॉगजेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.