คีย์เวิร์ด“ register” ใน C?


273

อะไรregisterคำหลักทำในภาษา C? ฉันได้อ่านแล้วว่ามันใช้สำหรับการปรับให้เหมาะสม แต่ไม่ได้กำหนดไว้อย่างชัดเจนในมาตรฐานใด ๆ มันยังเกี่ยวข้องหรือไม่และถ้าเป็นเช่นนั้นคุณจะใช้มันเมื่อไหร่?


42
คีย์เวิร์ดการลงทะเบียนทำอะไรใน C ถูกละเว้น :)
bestsss

19
@bestsss ไม่สนใจอย่างสมบูรณ์ ลองรับที่อยู่ของregisterตัวแปร
qrdl

4
โค้ดที่คุณกำลังอ่านนั้นเก่า youtube.com/watch?v=ibF36Yyeehw#t=1827
พันเอก Panic

คำตอบ:


341

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

คอมไพเลอร์สมัยใหม่ส่วนใหญ่ทำโดยอัตโนมัติและดีกว่าที่จะเลือกพวกเรามากกว่ามนุษย์


18
ฉันทดลองทดลองลงทะเบียนเพื่อรับผลงาน ACM ของฉันและบางครั้งก็ช่วยได้จริง แต่คุณต้องระวังตัวจริงเพราะตัวเลือกที่ไม่ดีลดประสิทธิภาพลง
ypnos

81
เหตุผลที่ดีที่จะไม่ใช้ 'register': คุณไม่สามารถระบุที่อยู่ของตัวแปรที่ประกาศว่า 'register'
Adam Rosenfield

23
โปรดทราบว่าคอมไพเลอร์บางส่วน / หลายคนจะไม่สนใจคีย์เวิร์ดการลงทะเบียน (ซึ่งถูกกฎหมายอย่างสมบูรณ์)
Euro Micelli

5
ypnos: ที่จริงแล้วความเร็วของโซลูชันสำหรับปัญหา ACM ICPC นั้นขึ้นอยู่กับการเลือกอัลกอริทึมมากกว่าการปรับให้เหมาะสมแบบไมโครเช่นนั้น การ จำกัด เวลา 5 วินาทีนั้นเพียงพอสำหรับการแก้ปัญหาที่ถูกต้องโดยเฉพาะเมื่อใช้ C แทน Java
Joey

66
@Euro: คุณอาจจะรู้เรื่องนี้ แต่เพียงเพื่อให้ชัดเจนต้องรวบรวมเพื่อป้องกันที่อยู่ของregisterตัวแปรจากการถูกนำมา; นี้เป็นเพียงผลบังคับของregisterคำหลัก แม้สิ่งนี้จะเพียงพอในการปรับปรุงการปรับให้เหมาะสมเพราะมันไม่สำคัญที่จะบอกว่าตัวแปรสามารถแก้ไขได้ภายในฟังก์ชั่นนี้เท่านั้น
Dale Hagglund

69

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

ดังนั้นการใช้registerคุณจะไม่ได้อะไรเลย (คอมไพเลอร์จะตัดสินใจเองว่าจะวางตัวแปรไว้ที่ใด) และเสีย&โอเปอเรเตอร์ - ไม่ต้องใช้เหตุผล


94
มีเหตุผลจริงๆ ข้อเท็จจริงที่ว่าคุณไม่สามารถใช้ที่อยู่ของตัวแปรทำให้เกิดโอกาสในการเพิ่มประสิทธิภาพบางอย่าง: คอมไพเลอร์สามารถพิสูจน์ได้ว่าตัวแปรจะไม่ถูกใช้นามแฝง
Alexandre C.

8
คอมไพเลอร์น่ากลัวที่พิสูจน์ได้ว่านามแฝงไม่ได้เกิดขึ้นในกรณีที่ไม่สำคัญดังนั้นจึงregisterมีประโยชน์สำหรับสิ่งนี้แม้ว่าคอมไพเลอร์ไม่ได้ใส่ไว้ในทะเบียน
Miles Rout

2
@AlexandreC, Miles, compilers สามารถตกลงได้อย่างสมบูรณ์แบบในการตรวจสอบว่ามีการใช้ตัวแปรหรือไม่ ดังนั้นไม่ว่าจะมีปัญหาอื่นเกี่ยวกับการตรวจหานามแฝงหรือไม่ก็ตาม เมื่อ K + R สร้าง C ขึ้นครั้งแรกมันมีประโยชน์จริง ๆ ที่จะรู้ล่วงหน้าว่า & จะไม่ถูกใช้เนื่องจากคอมไพเลอร์นั้นได้ทำการตัดสินใจการจัดสรรการลงทะเบียนเพื่อดูการประกาศก่อนที่จะดูรหัสต่อไปนี้ นั่นคือเหตุผลที่ห้ามอยู่ในสถานที่ คำหลัก 'register' นั้นล้าสมัยแล้วในตอนนี้
greggo

25
โดยตรรกะconstนี้ก็ไร้ประโยชน์เพราะมันชนะคุณไม่มีอะไรคุณเพียงแค่สูญเสียความสามารถในการเปลี่ยนตัวแปร registerสามารถใช้เพื่อให้แน่ใจว่าไม่มีใครรับที่อยู่ของตัวแปรในอนาคตโดยไม่ต้องคิด ฉันไม่เคยมีเหตุผลที่จะใช้registerแต่
Tor Klingberg

34

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


8
มูลค่าเพิ่มสำหรับผู้ใช้ C ++, C ++ ช่วยให้คุณสามารถระบุที่อยู่ของตัวแปร register
Will

5
@Will: ... แต่คอมไพเลอร์มีแนวโน้มที่จะไม่สนใจคำหลักนั้นเป็นผล ดูคำตอบของฉัน
bwDraco

ใช่ดูเหมือนว่า 'register' เป็นยาหลอกใน C ++ แต่มีเพียงเพื่อให้สามารถรวบรวมรหัส C เป็น C ++ ได้ และจะไม่มีเหตุผลมากที่จะห้าม & var ในขณะที่อนุญาตให้ส่งผ่านโดยอ้างอิงหรืออ้างอิงกลุ่มอ้างอิงและหากไม่มีการส่งต่ออ้างอิงคุณได้ทำลาย C ++ อย่างจริงจัง
greggo

22

ฉันรู้ว่าคำถามนี้เกี่ยวกับ C แต่คำถามเดียวกันสำหรับ C ++ ถูกปิดเหมือนสำเนาที่แน่นอนของคำถามนี้ คำตอบนี้อาจไม่ใช้สำหรับ C.


ร่างล่าสุดของมาตรฐาน C ++ 11, N3485กล่าวถึงสิ่งนี้ใน 7.1.1 / 3:

ตัวregisterระบุเป็นคำใบ้สำหรับการนำไปใช้ซึ่งตัวแปรที่ประกาศดังนั้นจะถูกใช้อย่างหนัก [ หมายเหตุ:คำใบ้สามารถถูกละเว้นและในการนำไปใช้งานส่วนใหญ่มันจะถูกละเว้นหากที่อยู่ของตัวแปรถูกนำมาใช้ การใช้งานนี้เลิกใช้แล้ว ... - บันทึกย่อ ]

ใน C ++ ( แต่ไม่ได้ใน C) มาตรฐานไม่ได้ระบุว่าคุณไม่สามารถใช้ที่อยู่ของตัวแปรประกาศregister; อย่างไรก็ตามเนื่องจากตัวแปรที่เก็บอยู่ใน CPU register ตลอดอายุการใช้งานของมันไม่มีตำแหน่งหน่วยความจำที่สัมพันธ์กับมันการพยายามใช้ที่อยู่ของมันจะไม่ถูกต้องและคอมไพเลอร์จะเพิกเฉยต่อregisterคำหลักเพื่อให้สามารถรับที่อยู่ได้


17

มันไม่ได้เกี่ยวข้องกับอย่างน้อย 15 ปีเป็นเครื่องมือเพิ่มประสิทธิภาพในการตัดสินใจเกี่ยวกับเรื่องนี้ดีกว่าที่คุณสามารถทำได้ แม้ว่ามันจะมีความเกี่ยวข้อง แต่ก็มีความหมายมากกว่าสำหรับสถาปัตยกรรม CPU ที่มีการลงทะเบียนจำนวนมากเช่น SPARC หรือ M68000 มากกว่าที่ Intel ทำกับการลงทะเบียนซึ่งส่วนใหญ่จะถูกสงวนไว้โดยคอมไพเลอร์เพื่อจุดประสงค์ของตัวเอง


13

ที่จริง register บอกคอมไพเลอร์ว่าตัวแปรไม่ได้ใช้นามแฝงกับสิ่งอื่นใดในโปรแกรม (ไม่ใช่ของ char)

ที่สามารถใช้ประโยชน์จากคอมไพเลอร์สมัยใหม่ในสถานการณ์ที่หลากหลายและสามารถช่วยคอมไพเลอร์ได้ค่อนข้างซับซ้อนในรหัส - ในรหัสง่าย ๆ คอมไพเลอร์สามารถเข้าใจสิ่งนี้ด้วยตนเอง

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


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

1
@greggo: แย่เกินไปregisterห้ามใช้ที่อยู่เลยเพราะมิฉะนั้นมันจะมีประโยชน์ที่จะให้คอมไพเลอร์ทราบว่ากรณีใดที่คอมไพเลอร์จะสามารถใช้การเพิ่มประสิทธิภาพการลงทะเบียนแม้ที่อยู่ของตัวแปรจะถูกส่งไปยังฟังก์ชันภายนอก (ตัวแปรจะต้อง จะถูกฟลัชไปยังหน่วยความจำสำหรับการเรียกนั้น แต่เมื่อฟังก์ชั่นกลับมาคอมไพเลอร์สามารถปฏิบัติต่อมันอีกครั้งเป็นตัวแปรที่ไม่เคยมีที่อยู่)
supercat

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

1
@greggo: บอกว่าถ้าbarเป็นregisterตัวแปรคอมไพเลอร์อาจที่พักผ่อนของแทนที่foo(&bar);ด้วยint temp=bar; foo(&temp); bar=temp;แต่การที่อยู่ของbarจะเป็นสิ่งต้องห้ามในบริบทอื่น ๆ ส่วนใหญ่จะไม่ดูเหมือนเป็นกฎที่มีความซับซ้อนมากเกินไป หากตัวแปรนั้นสามารถถูกเก็บไว้ในรีจิสเตอร์การทดแทนจะทำให้โค้ดเล็กลง ถ้าตัวแปรจะต้องถูกเก็บไว้ใน RAM อยู่แล้วการแทนที่จะทำให้โค้ดใหญ่ขึ้น การปล่อยให้คำถามว่าจะทำการทดแทนจนถึงคอมไพเลอร์จะทำให้โค้ดดีขึ้นในทั้งสองกรณีหรือไม่
supercat

1
@greggo: การอนุญาตให้มีการregisterรับรองกับตัวแปรทั่วโลกไม่ว่าคอมไพเลอร์จะอนุญาตให้ใช้ที่อยู่หรือไม่ก็ตามจะช่วยให้การเพิ่มประสิทธิภาพที่ดีในกรณีที่ฟังก์ชั่นอินไลน์ที่ใช้ตัวแปรทั่วโลกถูกเรียกซ้ำในลูป ฉันไม่สามารถคิดวิธีอื่นเพื่อให้ตัวแปรนั้นถูกเก็บไว้ในการลงทะเบียนระหว่างการวนซ้ำ - คุณได้ไหม
supercat

13

ฉันได้อ่านแล้วว่ามันใช้สำหรับการปรับให้เหมาะสม แต่ไม่ได้กำหนดไว้อย่างชัดเจนในมาตรฐานใด ๆ

ในความเป็นจริงมันถูกกำหนดไว้อย่างชัดเจนโดยมาตรฐาน C การอ้างอิงร่าง N1570ส่วน 6.7.1 วรรค 6 (เวอร์ชันอื่นมีข้อความเดียวกัน):

การประกาศตัวระบุสำหรับวัตถุที่มีตัวระบุระดับการจัดเก็บregisterแนะนำการเข้าถึงวัตถุให้เร็วที่สุด ขอบเขตของข้อเสนอแนะที่มีประสิทธิภาพนั้นถูกกำหนดโดยการนำไปปฏิบัติ

ชาวเอก &โอเปอเรเตอร์อาจไม่สามารถใช้กับวัตถุที่กำหนดด้วยregisterและregisterอาจไม่สามารถใช้ในการประกาศภายนอก

มีกฎอื่น ๆ อีกสองสาม (ค่อนข้างชัดเจน) ที่เฉพาะเจาะจง registerวัตถุที่ผ่านการรับรอง:

  • การกำหนดวัตถุอาร์เรย์ด้วยregisterมีพฤติกรรมที่ไม่ได้กำหนด
    การแก้ไข:การกำหนดวัตถุอาร์เรย์ด้วยถูกต้องตามกฎหมายregisterแต่คุณไม่สามารถทำอะไรที่มีประโยชน์กับวัตถุดังกล่าวได้ (การทำดัชนีในอาร์เรย์ต้องใช้ที่อยู่ขององค์ประกอบเริ่มต้น)
  • _Alignasระบุ (ใหม่ใน C11) อาจไม่สามารถนำไปใช้กับวัตถุดังกล่าวได้
  • หากชื่อพารามิเตอร์ที่ส่งไปยังva_startแมโครนั้นมีregisterคุณสมบัติ -qualified พฤติกรรมจะไม่ได้กำหนด

อาจมีบางคน ดาวน์โหลดร่างมาตรฐานและค้นหา "ลงทะเบียน" หากคุณสนใจ

ตามความหมายของชื่อความหมายดั้งเดิมของregisterการจะต้องมีวัตถุที่จะเก็บไว้ใน CPU register แต่ด้วยการปรับปรุงในการปรับแต่งคอมไพเลอร์สิ่งนี้จึงมีประโยชน์น้อยลง มาตรฐาน C รุ่นใหม่ไม่ได้อ้างถึง CPU register เพราะไม่จำเป็นต้องใช้อีกต่อไปสมมติว่ามีสิ่งนั้นอยู่ (มีสถาปัตยกรรมที่ไม่ได้ใช้รีจิสเตอร์) ภูมิปัญญาทั่วไปคือการนำregisterไปใช้กับการประกาศวัตถุมีแนวโน้มที่จะเลวลงรหัสที่สร้างขึ้นเพราะมันรบกวนการจัดสรรลงทะเบียนของคอมไพเลอร์ของตัวเอง อาจมีบางกรณีที่มีประโยชน์ (เช่นถ้าคุณรู้ว่าตัวแปรเข้าถึงบ่อยแค่ไหนและความรู้ของคุณดีกว่าสิ่งที่คอมไพเลอร์การปรับให้เหมาะสมที่ทันสมัยสามารถหาได้)

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


พฤติกรรมของโปรแกรมนี้ไม่ได้กำหนดจริงๆตามมาตรฐาน C หรือไม่ มันถูกกำหนดไว้อย่างดีใน C ++ หรือไม่ ฉันคิดว่ามันชัดเจนใน C ++
Destructor

@Destructor: ทำไมมันถึงไม่ได้กำหนด? ไม่มีregisterอาเรย์วัตถุที่ผ่านการรับรองถ้านั่นคือสิ่งที่คุณคิด
Keith Thompson

โอ้ฉันลืมเขียนคำสำคัญลงทะเบียนในประกาศอาร์เรย์ใน main () มันถูกกำหนดไว้อย่างดีใน C ++ หรือไม่
Destructor

ฉันคิดผิดเกี่ยวกับการกำหนดregisterวัตถุอาร์เรย์ ดูสัญลักษณ์แสดงหัวข้อแรกที่อัปเดตในคำตอบของฉัน มันถูกกฎหมายในการกำหนดวัตถุดังกล่าว แต่คุณไม่สามารถทำอะไรกับมันได้ หากคุณเพิ่มregisterคำจำกัดความsในตัวอย่างของคุณโปรแกรมนั้นผิดกฎหมาย (การละเมิดข้อ จำกัด ) ใน C. C ++ ไม่ได้มีข้อ จำกัด เหมือนกันregisterดังนั้นโปรแกรมจะเป็น C ++ ที่ถูกต้อง (แต่การใช้registerจะไม่มีจุดหมาย)
Keith Thompson

@ KeithThompson: registerคำหลักสามารถใช้เพื่อวัตถุประสงค์ที่เป็นประโยชน์หากถูกกฎหมายในการใช้ที่อยู่ของตัวแปรดังกล่าว แต่เฉพาะในกรณีที่ความหมายจะไม่ได้รับผลกระทบโดยการคัดลอกตัวแปรไปยังชั่วคราวเมื่อมีการใช้ที่อยู่และโหลดซ้ำจากชั่วคราว ที่จุดลำดับถัดไป ที่จะช่วยให้คอมไพเลอร์สันนิษฐานว่าตัวแปรอาจถูกเก็บไว้อย่างปลอดภัยในการลงทะเบียนในการเข้าถึงตัวชี้ทั้งหมดโดยมีการล้างข้อมูลในสถานที่ใด ๆ
supercat

9

Storytime!

C เป็นภาษาที่เป็นนามธรรมของคอมพิวเตอร์ ช่วยให้คุณสามารถทำสิ่งต่าง ๆ ในสิ่งที่คอมพิวเตอร์ทำนั่นคือจัดการความจำทำคณิตศาสตร์พิมพ์สิ่งต่าง ๆ เป็นต้น

แต่ C เป็นเพียงนามธรรม และท้ายที่สุดสิ่งที่แยกออกมาจากคุณคือภาษาแอสเซมบลี แอสเซมบลีเป็นภาษาที่ CPU อ่านและถ้าคุณใช้คุณจะทำสิ่งต่าง ๆ ในแง่ของ CPU CPU ทำอะไรได้บ้าง โดยทั่วไปจะอ่านจากหน่วยความจำทำคณิตศาสตร์และเขียนไปยังหน่วยความจำ ซีพียูไม่เพียง แต่คำนวณเลขบนหน่วยความจำ ก่อนอื่นคุณต้องย้ายตัวเลขจากหน่วยความจำไปยังหน่วยความจำภายใน CPU ที่เรียกว่าregister. เมื่อคุณทำสิ่งที่คุณต้องทำกับหมายเลขนี้เสร็จแล้วคุณสามารถย้ายกลับไปที่หน่วยความจำระบบปกติ เหตุใดจึงต้องใช้หน่วยความจำระบบเลย การลงทะเบียนมีจำนวน จำกัด คุณจะได้รับตัวประมวลผลที่ทันสมัยเพียงประมาณร้อยไบต์เท่านั้นและตัวประมวลผลที่ได้รับความนิยมรุ่นเก่านั้นมีจำนวน จำกัด ยิ่งกว่า (6502 มีการลงทะเบียน 8 บิต 3 บิตสำหรับการใช้งานฟรี) ดังนั้นการดำเนินการทางคณิตศาสตร์โดยเฉลี่ยของคุณดูเหมือนว่า:

load first number from memory
load second number from memory
add the two
store answer into memory

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

เมื่อคุณประกาศตัวแปร registerคุณกำลังบอกคอมไพเลอร์ "โย่ฉันตั้งใจจะให้ตัวแปรนี้ใช้บ่อยและ / หรืออายุสั้นถ้าฉันเป็นคุณฉันจะพยายามเก็บไว้ในทะเบียน" เมื่อมาตรฐาน C บอกว่าคอมไพเลอร์ไม่ต้องทำอะไรจริง ๆ นั่นเป็นเพราะมาตรฐาน C ไม่รู้คอมพิวเตอร์ที่คุณกำลังคอมไพล์และมันอาจจะเหมือนกับ 6502 ข้างต้นซึ่งทั้ง 3 รีจิสเตอร์จำเป็นต้องทำงาน และไม่มีการลงทะเบียนอะไหล่เพื่อเก็บหมายเลขของคุณ อย่างไรก็ตามเมื่อมีข้อความระบุว่าคุณไม่สามารถใช้ที่อยู่ได้นั่นเป็นเพราะการลงทะเบียนไม่มีที่อยู่ พวกเขาเป็นมือของโปรเซสเซอร์ เนื่องจากคอมไพเลอร์ไม่จำเป็นต้องให้ที่อยู่แก่คุณและเนื่องจากมันไม่สามารถมีที่อยู่ได้เลยการเพิ่มประสิทธิภาพหลายอย่างจึงเปิดให้คอมไพเลอร์ มันสามารถพูดเก็บหมายเลขในการลงทะเบียนเสมอ มันไม่ได้ ไม่ต้องกังวลว่าจะเก็บไว้ที่ไหนในหน่วยความจำคอมพิวเตอร์ (ไม่จำเป็นต้องกลับไปใช้อีก) มันสามารถเปลี่ยนเป็นตัวแปรอื่นมอบมันให้กับโพรเซสเซอร์อื่นให้มันเปลี่ยนตำแหน่ง ฯลฯ

tl; dr: ตัวแปรอายุสั้นที่ใช้คณิตศาสตร์จำนวนมาก อย่าประกาศมากเกินไปในคราวเดียว


5

คุณกำลังยุ่งกับอัลกอริทึมกราฟสีของคอมไพเลอร์ ใช้สำหรับการจัดสรรการลงทะเบียน ส่วนใหญ่แล้ว มันทำหน้าที่เป็นคำแนะนำสำหรับคอมไพเลอร์ - มันเป็นเรื่องจริง แต่อย่าเพิกเฉยเนื่องจากคุณไม่ได้รับอนุญาตให้ใช้ที่อยู่ของตัวแปร register (โปรดจำไว้ว่าคอมไพเลอร์ซึ่งตอนนี้อยู่ในความเมตตาของคุณแล้วจะพยายามทำหน้าที่ต่างออกไป) ซึ่งในทางหนึ่งกำลังบอกคุณว่าอย่าใช้มัน

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

แต่อย่างที่ฉันบอกว่าเลิกใช้ไม่ได้หมายความว่าคุณไม่สามารถใช้งานได้


13
ฮาร์ดแวร์เก่าบางรุ่นมีการลงทะเบียนมากกว่าเครื่องของ Intel ที่ทันสมัย จำนวนการลงทะเบียนไม่มีส่วนเกี่ยวข้องกับอายุและทุกอย่างที่เกี่ยวข้องกับสถาปัตยกรรม CPU
เพียงความคิดเห็นที่ถูกต้องของฉัน

2
@JUSTMYcorrectOPINION แท้จริงแล้ว X86 นั้นมีทั้งหมดหกหลักโดยเหลือเพียง 1 หรือ 2 เพื่ออุทิศให้กับ 'register' ในความเป็นจริงเนื่องจากมีการเขียนหรือโอนรหัสจำนวนมากไปยังเครื่องลงทะเบียนไม่ดีฉันจึงสงสัยว่าสิ่งนี้มีส่วนสำคัญอย่างมากต่อคำหลัก 'ลงทะเบียน' กลายเป็นยาหลอก - ไม่มีจุดในการบอกใบ้ทะเบียนเมื่อไม่มี ที่นี่เรามีอายุ 4 ปีขึ้นไปและขอบคุณ x86_64 ที่เพิ่มขึ้นเป็น 14 และ ARM ก็เป็นสิ่งที่ยิ่งใหญ่เช่นกัน
greggo

4

เพียงแค่การสาธิตเล็กน้อย (โดยไม่มีจุดประสงค์ในโลกแห่งความจริง) สำหรับการเปรียบเทียบ: เมื่อลบregisterคำหลักก่อนแต่ละตัวแปรชิ้นส่วนของรหัสนี้จะใช้เวลา 3.41 วินาทีใน i7 ของฉัน (GCC) ด้วย registerรหัสเดียวกันเสร็จสมบูรณ์ใน 0.7 วินาที

#include <stdio.h>

int main(int argc, char** argv) {

     register int numIterations = 20000;    

     register int i=0;
     unsigned long val=0;

    for (i; i<numIterations+1; i++)
    {
        register int j=0;
        for (j;j<i;j++) 
        {
            val=j+i;
        }
    }
    printf("%d", val);
    return 0;
}

2
ด้วย gcc 4.8.4 และ -O3 ฉันไม่แตกต่างกัน โดยไม่ต้อง -O3 และ 40000 ซ้ำฉันได้รับอาจจะ 50 มิลลิวินาทีน้อยลงในช่วงเวลาที่ทั้งหมด 1.5s แต่ผมไม่ได้เรียกใช้เวลาพอที่จะรู้ว่าเป็นยังสำคัญ statistaclly
zstewart

ไม่มีความแตกต่างกับ CLANG 5.0 แพลตฟอร์มคือ AMD64 (ฉันได้ตรวจสอบเอาต์พุต ASM)
ern0

4

ฉันได้ทดสอบคีย์เวิร์ด register ภายใต้ QNX 6.5.0 โดยใช้รหัสต่อไปนี้:

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/neutrino.h>
#include <sys/syspage.h>

int main(int argc, char *argv[]) {
    uint64_t cps, cycle1, cycle2, ncycles;
    double sec;
    register int a=0, b = 1, c = 3, i;

    cycle1 = ClockCycles();

    for(i = 0; i < 100000000; i++)
        a = ((a + b + c) * c) / 2;

    cycle2 = ClockCycles();
    ncycles = cycle2 - cycle1;
    printf("%lld cycles elapsed\n", ncycles);

    cps = SYSPAGE_ENTRY(qtime) -> cycles_per_sec;
    printf("This system has %lld cycles per second\n", cps);
    sec = (double)ncycles/cps;
    printf("The cycles in seconds is %f\n", sec);

    return EXIT_SUCCESS;
}

ฉันได้ผลลัพธ์ดังต่อไปนี้:

-> ผ่านไป 807679611 รอบ

-> ระบบนี้มี 33,008,300 รอบต่อวินาที

-> รอบในไม่กี่วินาทีคือ ~ 0.244600

และตอนนี้ไม่ต้องลงทะเบียน int:

int a=0, b = 1, c = 3, i;

ฉันได้:

-> ผ่านไปแล้ว 1421694077 รอบ

-> ระบบนี้มี 33,008,300 รอบต่อวินาที

-> รอบในไม่กี่วินาทีคือ ~ 0.430700


2

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

ทุกวันนี้สิ่งนี้ไม่ได้มีประโยชน์มากนักเนื่องจากเครื่องมือเพิ่มประสิทธิภาพคอมไพเลอร์ส่วนใหญ่ดีกว่าคุณในการพิจารณาว่าควรใช้รีจิสเตอร์สำหรับตัวแปรนั้นหรือไม่และนานเท่าไหร่


2

ในช่วงอายุเจ็ดสิบที่จุดเริ่มต้นของภาษา C คีย์เวิร์ด register ได้ถูกนำเสนอเพื่ออนุญาตให้โปรแกรมเมอร์ให้คำแนะนำแก่คอมไพเลอร์โดยบอกว่าตัวแปรจะถูกใช้บ่อยมากและควรจะฉลาดเพื่อ รักษาความคุ้มค่าไว้ในการลงทะเบียนภายในของโปรเซสเซอร์

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

ผู้คนมากมายผิดแนะนำไม่ให้ใช้คีย์เวิร์ด register

มาดูกันว่าทำไม!

คำสำคัญการลงทะเบียนมีผลข้างเคียงที่เกี่ยวข้อง: คุณไม่สามารถอ้างอิง (รับที่อยู่ของ) ตัวแปรประเภทการลงทะเบียน

ผู้คนที่ให้คำแนะนำผู้อื่นไม่ให้ใช้รีจิสเตอร์ถือเป็นข้อโต้แย้งเพิ่มเติม

อย่างไรก็ตามความจริงง่ายๆที่รู้ว่าคุณไม่สามารถใช้ที่อยู่ของตัวแปร register ทำให้คอมไพเลอร์ (และเครื่องมือเพิ่มประสิทธิภาพ) รู้ว่าค่าของตัวแปรนี้ไม่สามารถแก้ไขได้ทางอ้อมผ่านตัวชี้

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

ทำแบบทดสอบของคุณเองและคุณจะได้รับการปรับปรุงประสิทธิภาพที่สำคัญในลูปด้านในของคุณ

c_register_side_effect_performance_boost


1

ในคอมไพเลอร์ C ที่รองรับจะพยายามปรับรหัสให้เหมาะสมเพื่อให้ค่าของตัวแปรนั้นอยู่ในการลงทะเบียนตัวประมวลผลจริง


1

คอมไพเลอร์ Visual C ++ ของ Microsoft จะเพิกเฉยregisterคำสำคัญเมื่อการปรับให้เหมาะสมการจัดสรรส่วนกลางลงทะเบียน (เปิดใช้งาน / การตั้งค่าสถานะคอมไพเลอร์ / Oe)

ดูลงทะเบียนคำสำคัญใน MSDN


1

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


0

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

อีกอย่างคือถ้าคุณประกาศตัวแปรในฐานะ register คุณจะไม่สามารถรับที่อยู่ของมันได้เนื่องจากมันไม่ได้ถูกเก็บไว้ในหน่วยความจำ มันได้รับการจัดสรรใน CPU register


0

gcc 9.3 เอาต์พุต asm โดยไม่ใช้แฟล็กการเพิ่มประสิทธิภาพ

#include <stdio.h>
int main(void) {
  int i = 3;
  i++;
  printf("%d", i);
  return 0;
}
.LC0:
        .string "%d"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 3
        add     DWORD PTR [rbp-4], 1
        mov     eax, DWORD PTR [rbp-4]
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0
        leave 
        ret
#include <stdio.h>
int main(void) {
  register int i = 3;
  i++;
  printf("%d", i);
  return 0;
}
.LC0:
        .string "%d"
main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 8
        mov     ebx, 3
        add     ebx, 1
        mov     esi, ebx
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

กองกำลังนี้ebxจะใช้สำหรับการคำนวณซึ่งหมายความว่าจะต้องมีการผลักไปยังสแต็กและเรียกคืนในตอนท้ายของฟังก์ชั่นเพราะมันถูกบันทึกไว้ callee registerผลิตบรรทัดที่มากขึ้นของรหัสและ 1 เขียนหน่วยความจำและหน่วยความจำ 1 อ่าน (แม้ว่าแนบเนียนนี้อาจได้รับการปรับให้เป็น 0 R / Ws ถ้าคำนวณได้กระทำในesiซึ่งเป็นสิ่งที่เกิดขึ้นโดยใช้ C ++ 's const register) การไม่ใช้registerสาเหตุ 2 การเขียนและการอ่าน 1 ครั้ง (แม้ว่าการจัดเก็บการโหลดการส่งต่อจะเกิดขึ้นในการอ่าน) นี่เป็นเพราะค่าจะต้องมีอยู่และปรับปรุงโดยตรงบนสแต็กเพื่อให้ค่าที่ถูกต้องสามารถอ่านได้โดยที่อยู่ (ตัวชี้) registerไม่มีข้อกำหนดนี้และไม่สามารถชี้ได้ constและregisterโดยทั่วไปจะตรงกันข้ามกับการvolatileใช้งานvolatileจะแทนที่ const optimisations ที่ขอบเขตไฟล์และบล็อกและการregisterเพิ่มประสิทธิภาพที่ขอบเขต block const registerและregisterจะสร้างผลลัพธ์ที่เหมือนกันเพราะ const ไม่ได้ทำอะไรเลยใน C ที่ block-scope ดังนั้นจึงregisterใช้การปรับให้เหมาะสมเท่านั้น

ในเสียงดังกราวregisterจะถูกละเว้น แต่การconstเพิ่มประสิทธิภาพยังคงเกิดขึ้น

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