คีย์เวิร์ดการลงทะเบียนจะมีประโยชน์จริงใน C เมื่อใด


10

ฉันกำลังสับสนเกี่ยวกับการใช้registerคำหลักใน C. มันจะบอกกันโดยทั่วไปว่าการใช้งานไม่จำเป็นต้องเหมือนในคำถามนี้ใน StackOverflow

คำหลักนี้ซ้ำซ้อนทั้งหมดใน C เนื่องจากคอมไพเลอร์สมัยใหม่หรือมีสถานการณ์ที่ยังคงมีประโยชน์หรือไม่ ถ้าใช่มีสถานการณ์อะไรบ้างที่การใช้registerคำหลักมีประโยชน์จริง ๆ


4
ฉันคิดว่าคำถามที่เชื่อมโยงและคำตอบนั้นเหมือนที่คุณคาดหวังไว้ที่นี่ ดังนั้นจะไม่มีข้อมูลใหม่ที่คุณสามารถรับได้ที่นี่
Uwe Plonus

@UwePlonus ฉันคิดเหมือนกันเกี่ยวกับconstคำหลัก แต่คำถามนี้พิสูจน์ว่าฉันผิด ดังนั้นฉันจะรอและดูสิ่งที่ฉันได้รับ
Aseem Bansal

ฉันคิดว่าconstคำหลักนั้นต่างจากการลงทะเบียน
Uwe Plonus

4
มันมีประโยชน์ถ้าคุณย้อนเวลากลับไปโดยไม่ตั้งใจและถูกบังคับให้ใช้คอมไพเลอร์ต้นซีตัวใดตัวหนึ่ง นอกจากนั้นมันไม่มีประโยชน์เลยมันล้าสมัยไปแล้วหลายปี
JohnB

@UwePlonus ฉันเพิ่งหมายความว่าอาจมีสถานการณ์ที่ไม่รู้จักกับฉันซึ่งคำหลักอาจมีประโยชน์
Aseem Bansal

คำตอบ:


11

มันไม่ซ้ำซ้อนในแง่ของภาษาเพียงแค่ใช้มันคุณกำลังบอกคอมไพเลอร์คุณจะ "ชอบ" ที่จะมีตัวแปรที่เก็บไว้ในรีจิสเตอร์ อย่างไรก็ตามมีการรับประกันเป็นศูนย์อย่างแน่นอนว่าสิ่งนี้จะเกิดขึ้นจริงในระหว่างรันไทม์


9
ยิ่งไปกว่านั้นมันก็มักจะเป็นกรณีที่คอมไพเลอร์รู้ดีที่สุดและคุณกำลังหายใจอยู่
Daniel Gratzer

6
@jozefg: ยิ่งแย่ลง คุณมีความเสี่ยงที่คอมไพเลอร์ให้เกียรติคำขอ / คำใบ้ของคุณและทำให้โค้ดแย่ลง
Bart van Ingen Schenau

9

ดังที่ได้กล่าวมาแล้วเครื่องมือเพิ่มประสิทธิภาพคอมไพเลอร์ทำให้registerคำหลักล้าสมัยเพื่อวัตถุประสงค์อื่นนอกเหนือจากการป้องกันนามแฝง อย่างไรก็ตามมีฐานโค้ดทั้งหมดที่คอมไพล์ด้วยการปรับให้เหมาะสมปิดอยู่ ( -O0ในgcc-speak ) สำหรับรหัสดังกล่าวregisterคำหลักสามารถมีผลอย่างมาก โดยเฉพาะอย่างยิ่งตัวแปรที่จะได้รับสล็อตบนสแต็ก (เช่นพารามิเตอร์ฟังก์ชั่นทั้งหมดและตัวแปรอัตโนมัติ) อาจถูกวางโดยตรงในการลงทะเบียนถ้าประกาศด้วยregisterคำหลัก

นี่คือตัวอย่างในโลกแห่งความเป็นจริง: สมมติว่ามีการดึงฐานข้อมูลบางอย่างเกิดขึ้นและรหัสการดึงข้อมูลได้ยัด tuple ที่ดึงมาไว้ใน C struct นอกจากนี้สมมติว่าเซตย่อยบางส่วนของโครงสร้าง C นี้จำเป็นต้องคัดลอกไปยังโครงสร้างอื่นบางทีโครงสร้างที่สองนี้อาจเป็นระเบียนแคชที่แสดงถึงข้อมูลเมตาที่เก็บอยู่ในฐานข้อมูลนั้นเนื่องจากข้อ จำกัด ของหน่วยความจำจะแคชชุดย่อยของแต่ละระเบียนข้อมูลเมตาเท่านั้น ในฐานข้อมูล

รับฟังก์ชั่นที่จะใช้เป็นตัวชี้ไปยังแต่ละประเภทของโครงสร้างและมีงาน แต่เพียงผู้เดียวก็คือการคัดลอกสมาชิกบางส่วนจากโครงสร้างเริ่มต้นไปยังโครงสร้างที่สอง: ตัวแปรตัวชี้โครงสร้างจะอยู่บนสแต็ก เมื่อการมอบหมายเกิดขึ้นจากสมาชิกของ struct หนึ่งไปยังอีกที่อยู่ที่อยู่ของ struct จะถูกโหลดเข้าสู่การลงทะเบียนเพื่อทำการเข้าถึงสมาชิกของ struct ที่กำลังคัดลอก หากจะต้องประกาศพอยน์เตอร์ struct ด้วยregisterคำสำคัญที่อยู่ของโครงสร้างจะยังคงอยู่ในรีจิสเตอร์โดยตัดคำแนะนำโหลดที่อยู่เข้าสู่การลงทะเบียนสำหรับการมอบหมายแต่ละครั้งอย่างมีประสิทธิภาพ

โปรดจำไว้ว่าคำอธิบายข้างต้นใช้กับโค้ดที่ไม่ได้เพิ่มประสิทธิภาพ


6

คุณบอกคอมไพเลอร์ว่าคุณจะไม่ใช้ที่อยู่ของตัวแปรและคอมไพเลอร์สามารถทำให้การปรับให้เหมาะสมต่อไปดีขึ้น เท่าที่ฉันรู้คอมไพเลอร์สมัยใหม่ค่อนข้างสามารถตรวจสอบว่าตัวแปรสามารถ / ควรเก็บไว้ในทะเบียนหรือไม่

ตัวอย่าง:

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

การอ้างอิงหรือใช้ที่อยู่ของ?
Detly

@detly: ถูกต้องแน่นอน
ลูคัส

0

ในวันที่คอมพิวเตอร์ขนาด 16 บิตมักต้องลงทะเบียนหลายครั้งเพื่อดำเนินการคูณและหารแบบ 32 บิต เมื่อหน่วยจุดลอยตัวถูกรวมเข้ากับชิปและจากนั้นสถาปัตยกรรม 64- บิต 'เข้ายึด' ทั้งความกว้างของรีจิสเตอร์และจำนวนหน่วยขยาย ในที่สุดสิ่งนี้นำไปสู่การสร้างสถาปัตยกรรมที่สมบูรณ์ของ CPU ดูลงทะเบียนไฟล์บน Wikipedia

กล่าวโดยสรุปคุณจะต้องใช้เวลาพอสมควรในการพิจารณาว่าเกิดอะไรขึ้นหากคุณใช้ชิป X86 หรือ ARM 64 บิต หากคุณใช้ซีพียู 16 บิตที่ฝังตัวสิ่งนี้อาจช่วยคุณได้ อย่างไรก็ตามชิปฝังตัวขนาดเล็กส่วนใหญ่จะไม่ทำงานในเวลาที่สำคัญ - เตาอบไมโครเวฟของคุณอาจสุ่มตัวอย่างทัชแพดของคุณ 10,000 ครั้งต่อวินาที - ไม่มีสิ่งใดที่ทำให้ซีพียู 4Mhz ทำงานได้


1
4 MIPS / 10,000 โพล / วินาที = 400 คำแนะนำ / แบบสำรวจ นั่นไม่ใช่ระยะขอบเกือบเท่าที่คุณต้องการ โปรดทราบด้วยว่าโปรเซสเซอร์ 4 MHz จำนวนไม่กี่ตัวถูก microcoded ภายในซึ่งหมายความว่าไม่มีที่ไหนเลยใกล้ 1 MIP / MHz
John R. Strohm

@ JohnR.Strohm - อาจมีสถานการณ์ที่ใคร ๆ สามารถพิสูจน์ได้ว่ามีกี่รอบการเรียนการสอนที่จะใช้ แต่บ่อยครั้งที่วิธีที่ถูกกว่าตอนนี้คือการได้ชิปเร็วขึ้นและนำผลิตภัณฑ์ออกจากประตู ในตัวอย่างที่ให้มาแน่นอนว่าไม่จำเป็นต้องสุ่มตัวอย่างต่อไปที่ 10,000 หากมีคำสั่ง - มันอาจไม่ทำการสุ่มตัวอย่างต่อในช่วงไตรมาสที่สองโดยไม่มีอันตรายใด ๆ เกิดขึ้น มันกลายเป็นเรื่องยากมากขึ้นที่จะคิดออกว่าการเพิ่มประสิทธิภาพการเขียนโปรแกรมที่กำกับจะมีความสำคัญ
เมเรดิ ธ แย่

1
เป็นไปไม่ได้เสมอที่จะ "เพียงแค่รับชิปที่เร็วขึ้นและนำผลิตภัณฑ์ออกไปนอกประตู" พิจารณาการประมวลผลภาพตามเวลาจริง 640x480 พิกเซล / เฟรม x 60 เฟรม / วินาที x N คำแนะนำต่อพิกเซลเพิ่มได้อย่างรวดเร็ว (บทเรียนจากการประมวลผลภาพแบบเรียลไทม์คือคุณดูดซับเลือดของเมล็ดพิกเซลและคุณไม่สนใจทุกสิ่งทุกอย่างเพราะมันทำงานหนึ่งครั้งต่อบรรทัดหรือหนึ่งครั้งต่อแพทช์หรือหนึ่งครั้งต่อเฟรมเมื่อเทียบกับหลายร้อยครั้งต่อบรรทัดหรือ แพทช์หรือหมื่นหรือหลายแสนครั้งต่อเฟรม)
John R. Strohm

@ JohnR.Strohm - ตัวอย่างการประมวลผลภาพแบบเรียลไทม์ฉันจะเข้าใจว่าสภาพแวดล้อมขั้นต่ำคือ 32 บิต การออกไปที่ขา (เพราะฉันไม่รู้ว่ามันจะใช้ประโยชน์ได้จริงอย่างไร) ตัวเร่งความเร็วกราฟิกจำนวนมากที่มีอยู่ในชิปอาจใช้งานได้สำหรับการรับรู้ภาพดังนั้นชิป ARM (ตัวอย่าง) ที่มีเอ็นจิ้นการรวมอาจมี ALU เพิ่มเติม สำหรับการรับรู้ เมื่อถึงเวลานั้นการใช้คำสำคัญ 'ลงทะเบียน' เพื่อเพิ่มประสิทธิภาพเป็นปัญหาเล็ก ๆ
เมเรดิ ธ แย่

-3

ในการระบุว่าคีย์เวิร์ด register มีนัยสำคัญหรือไม่ตัวอย่างโค้ดขนาดจิ๋วจะไม่ทำ นี่คือรหัส c ที่แนะนำให้ฉันคำสำคัญการลงทะเบียนยังคงมีความสำคัญ แต่มันอาจแตกต่างกับ GCC บน Linux ฉันไม่รู้ จะลงทะเบียน int k & l ถูกเก็บไว้ใน CPU register หรือไม่? ผู้ใช้ Linux (โดยเฉพาะ) ควรรวบรวมกับ GCC และการเพิ่มประสิทธิภาพ ด้วย Borland bcc32 คีย์เวิร์ด register ดูเหมือนจะทำงาน (ในตัวอย่างนี้) เนื่องจาก & -operator จะให้รหัสข้อผิดพลาดสำหรับจำนวนเต็มที่ประกาศ บันทึก! นี่ไม่ใช่กรณีที่มีตัวอย่างเล็ก ๆ กับ Borland บน Windows! เพื่อที่จะดูว่าคอมไพเลอร์นั้นปรับให้เหมาะสมหรือไม่นั้นจะต้องเป็นตัวอย่างที่มากกว่า ลูปเปล่าจะไม่ทำ! อย่างไรก็ตาม - หากที่อยู่สามารถอ่านได้ด้วย & - ผู้ปฏิบัติงานตัวแปรจะไม่ถูกเก็บไว้ในการลงทะเบียน CPU แต่ถ้าตัวแปร register register ไม่สามารถอ่านได้ (ทำให้เกิดรหัสข้อผิดพลาดในการคอมไพล์) - ฉันต้องสมมติว่าคีย์เวิร์ด register นั้นทำให้ตัวแปรอยู่ใน CPU-register มันอาจแตกต่างกันไปตามแพลตฟอร์มต่าง ๆ ฉันไม่รู้ (หากใช้งานได้จำนวนของ "เห็บ" จะต่ำกว่ามากด้วยการประกาศการลงทะเบียน

/* 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 + = jj / ii;} - ขออภัยจริง ๆ สำหรับสิ่งนี้
John P Eriksson

ให้ k กับฉันเริ่มด้วย 1 - ไม่เป็นศูนย์ เสียใจมาก.
John P Eriksson

3
คุณสามารถแก้ไขคำตอบของคุณแทนการเขียนการแก้ไขในความคิดเห็น
Jan Doggen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.