Đăng ký từ khóa trong C?


272

registertừ khóa làm bằng ngôn ngữ C? Tôi đã đọc rằng nó được sử dụng để tối ưu hóa nhưng không được xác định rõ ràng trong bất kỳ tiêu chuẩn nào. Nó vẫn còn liên quan và nếu vậy, khi nào bạn sẽ sử dụng nó?


41
Từ khóa đăng ký làm gì trong C? bị bỏ qua :)
bestsss

18
@bestsss Không hoàn toàn bỏ qua. Cố gắng để có được một địa chỉ của registerbiến.
qrdl

4
Đó là mã bạn đang đọc là youtube.com/watch?v=ibF36Yyeehw#t=1827
Đại Tá Panic

Câu trả lời:


340

Đó là một gợi ý cho trình biên dịch rằng biến sẽ được sử dụng nhiều và bạn khuyên bạn nên giữ nó trong một thanh ghi bộ xử lý nếu có thể.

Hầu hết các trình biên dịch hiện đại làm điều đó một cách tự động, và tốt hơn trong việc chọn chúng hơn con người chúng ta.


17
Chà, tôi đã thử nghiệm đăng ký để điều chỉnh các bài nộp ACM của mình và đôi khi nó thực sự hữu ích. Nhưng bạn thực sự phải cẩn thận, bởi vì sự lựa chọn kém làm giảm hiệu suất.
ypnos

80
Một lý do chính đáng để không sử dụng 'đăng ký': bạn không thể lấy địa chỉ của một biến được khai báo là 'đăng ký'
Adam Rosenfield

22
Lưu ý rằng một số / nhiều trình biên dịch sẽ hoàn toàn bỏ qua từ khóa đăng ký (điều này là hoàn toàn hợp pháp).
Euro Micelli

4
ypnos: Trên thực tế tốc độ của một giải pháp cho các vấn đề ICPC ACM phụ thuộc nhiều vào sự lựa chọn thuật toán hơn là tối ưu hóa vi mô như vậy. Giới hạn thời gian 5 giây thường là đủ cho một giải pháp chính xác, đặc biệt là khi sử dụng C thay vì Java.
Joey

65
@Euro: Bạn có thể biết điều này, nhưng để rõ ràng, trình biên dịch được yêu cầu để ngăn chặn địa chỉ của một registerbiến được lấy; đây là hiệu ứng bắt buộc duy nhất của registertừ khóa. Thậm chí điều này là đủ để cải thiện tối ưu hóa, bởi vì nó trở nên tầm thường để nói rằng biến chỉ có thể được sửa đổi trong hàm này.
Dale Hagglund

69

Tôi ngạc nhiên khi không ai đề cập rằng bạn không thể lấy địa chỉ của biến đăng ký, ngay cả khi trình biên dịch quyết định giữ biến trong bộ nhớ thay vì trong thanh ghi.

Vì vậy, sử dụng registerbạn sẽ không có gì (dù sao trình biên dịch sẽ tự quyết định nơi đặt biến) và mất &toán tử - không có lý do để sử dụng nó.


94
Có một lý do thực sự. Thực tế là bạn không thể lấy địa chỉ của biến mang lại một số cơ hội tối ưu hóa: trình biên dịch có thể chứng minh rằng biến đó sẽ không được đặt bí danh.
Alexandre C.

8
Trình biên dịch nổi tiếng là khủng khiếp khi chứng minh rằng răng cưa không xảy ra trong các trường hợp không cần thiết, vì vậy rất registerhữu ích cho việc này ngay cả khi trình biên dịch không đưa nó vào một thanh ghi.
Miles Rout

2
@AlexandreC, Miles, trình biên dịch hoàn toàn ổn khi kiểm tra xem & có được lấy một biến ở bất cứ đâu không. Vì vậy, bất kể những khó khăn khác về việc phát hiện răng cưa, nghỉ ngơi mà không mua gì cho bạn. Khi K + R lần đầu tiên tạo C, thực sự hữu ích khi biết trước rằng & sẽ không được sử dụng, vì trình biên dịch đó thực sự đã đưa ra quyết định cấp phát đăng ký khi xem khai báo, trước khi xem mã sau đây. Đó là lý do tại sao các lệnh cấm được đưa ra. Từ khóa 'đăng ký' về cơ bản đã lỗi thời.
greggo

25
Theo logic constnày cũng vô dụng vì nó chẳng giúp gì cho bạn, bạn chỉ mất khả năng thay đổi một biến.registercó thể được sử dụng để đảm bảo không ai lấy địa chỉ của một biến trong tương lai mà không cần suy nghĩ. Tôi chưa bao giờ có lý do để sử dụng registermặc dù.
Tor Klingberg

34

Nó báo cho trình biên dịch thử sử dụng một thanh ghi CPU, thay vì RAM, để lưu trữ biến. Các thanh ghi nằm trong CPU và truy cập nhanh hơn nhiều so với RAM. Nhưng đó chỉ là một gợi ý cho trình biên dịch, và nó có thể không tuân theo.


8
Thêm giá trị cho những người sử dụng C ++, C ++ cho phép bạn lấy địa chỉ của biến đăng ký
Sẽ

5
@ Will: ... nhưng kết quả là trình biên dịch sẽ bỏ qua từ khóa. Xem câu trả lời của tôi.
bwDraco

Vâng, có vẻ như 'đăng ký' là một giả dược trong C ++, nó chỉ ở đó để cho phép mã C được biên dịch thành C ++. Và sẽ không có ý nghĩa gì khi cấm & var trong khi cho phép chuyển nó bằng tham chiếu hoặc tham chiếu const và không có tham chiếu qua bạn đã phá vỡ nghiêm trọng C ++.
greggo

22

Tôi biết câu hỏi này là về C, nhưng câu hỏi tương tự cho C ++ đã được đóng lại như là một bản sao chính xác của câu hỏi này. Câu trả lời này do đó có thể không áp dụng cho C.


Bản nháp mới nhất của tiêu chuẩn C ++ 11, N3485 , cho biết điều này trong 7.1.1 / 3:

Một registerspecifier là một gợi ý cho việc thực hiện rằng biến được khai báo sẽ được sử dụng nhiều. [ lưu ý: Gợi ý có thể bị bỏ qua và trong hầu hết các triển khai, nó sẽ bị bỏ qua nếu lấy địa chỉ của biến. Sử dụng này không được chấp ... -end note ]

Trong C ++ (nhưng không phải trong C), tiêu chuẩn không nêu rõ rằng bạn không thể lấy địa chỉ của một biến được khai báo register; tuy nhiên, vì một biến được lưu trữ trong một thanh ghi CPU trong suốt vòng đời của nó không có vị trí bộ nhớ được liên kết với nó, việc cố lấy địa chỉ của nó sẽ không hợp lệ và trình biên dịch sẽ bỏ qua registertừ khóa để cho phép lấy địa chỉ.


17

Nó đã không liên quan trong ít nhất 15 năm vì các trình tối ưu hóa đưa ra quyết định tốt hơn về điều này hơn bạn có thể. Ngay cả khi nó có liên quan, nó có ý nghĩa hơn đối với kiến ​​trúc CPU với rất nhiều thanh ghi, như SPARC hoặc M68000 so với Intel với các thanh ghi nhỏ, hầu hết được trình biên dịch dành riêng cho mục đích riêng của nó.


13

Trên thực tế, thanh ghi cho trình biên dịch biết rằng biến không bí danh với bất kỳ thứ gì khác trong chương trình (thậm chí không phải là char).

Điều đó có thể được khai thác bởi các trình biên dịch hiện đại trong nhiều tình huống khác nhau và có thể giúp trình biên dịch khá nhiều mã phức tạp - trong mã đơn giản, trình biên dịch có thể tự mình tìm ra điều này.

Mặt khác, nó không phục vụ mục đích và không được sử dụng để phân bổ đăng ký. Nó thường không phát sinh sự suy giảm hiệu suất để chỉ định nó, miễn là trình biên dịch của bạn đủ hiện đại.


"Nói với trình biên dịch .." không, nó không. Tất cả các biến tự động đều có thuộc tính đó, trừ khi bạn lấy địa chỉ của nó sử dụng nó theo những cách vượt quá mức sử dụng có thể phân tích nhất định. Vì vậy, trình biên dịch biết điều này từ mã, bất kể bạn có sử dụng từ khóa đăng ký hay không. Điều đó xảy ra khi từ khóa 'đăng ký' khiến việc viết một cấu trúc như vậy là bất hợp pháp, nhưng nếu bạn không sử dụng từ khóa và không lấy địa chỉ theo cách như vậy, thì trình biên dịch vẫn biết rằng nó an toàn. Thông tin như vậy là rất quan trọng để tối ưu hóa.
greggo

1
@greggo: Quá tệ khi registercấm lấy địa chỉ, vì nếu không thì có thể hữu ích cho trình biên dịch biết các trường hợp trình biên dịch có thể áp dụng tối ưu hóa đăng ký mặc dù địa chỉ của biến được truyền cho hàm bên ngoài (biến sẽ phải được xóa vào bộ nhớ cho cuộc gọi cụ thể đó , nhưng một khi hàm trả về, trình biên dịch lại có thể coi nó như một biến có địa chỉ chưa bao giờ được thực hiện).
supercat

@supercat Tôi nghĩ vẫn sẽ là một cuộc trò chuyện rất khó khăn với trình biên dịch. Nếu đó là những gì bạn muốn nói với trình biên dịch, bạn có thể thực hiện nó bằng cách sao chép biến thứ nhất sang biến thứ hai không có '&' trên đó, và sau đó không bao giờ sử dụng biến đầu tiên nữa.
greggo

1
@greggo: Nói rằng nếu barlà một registerbiến, trình biên dịch có thể thay thế foo(&bar);bằng int temp=bar; foo(&temp); bar=temp;cách sử dụng, nhưng việc lấy địa chỉ barsẽ bị cấm trong hầu hết các bối cảnh khác có vẻ không phải là một quy tắc quá phức tạp. Nếu biến có thể được giữ trong một thanh ghi, sự thay thế sẽ làm cho mã nhỏ hơn. Nếu biến số cần phải được giữ trong RAM, thì sự thay thế sẽ làm cho mã lớn hơn. Để lại câu hỏi liệu có nên thay thế trình biên dịch sẽ dẫn đến mã tốt hơn trong cả hai trường hợp.
supercat

1
@greggo: Cho phép một trình registerđộ về các biến toàn cục, cho dù trình biên dịch có cho phép lấy địa chỉ hay không, sẽ cho phép một số tối ưu hóa tốt trong trường hợp hàm nội tuyến sử dụng biến toàn cục được gọi lặp lại trong một vòng lặp. Tôi không thể nghĩ ra bất kỳ cách nào khác để cho biến đó được giữ trong một thanh ghi giữa các lần lặp lặp - bạn có thể không?
supercat

13

Tôi đã đọc rằng nó được sử dụng để tối ưu hóa nhưng không được xác định rõ ràng trong bất kỳ tiêu chuẩn nào.

Trong thực tế, nó được xác định rõ ràng bởi tiêu chuẩn C. Trích dẫn dự thảo N1570 phần 6.7.1 đoạn 6 (các phiên bản khác có cùng từ ngữ):

Một tuyên bố về một định danh cho một đối tượng với trình xác định lớp lưu trữ registercho thấy rằng việc truy cập vào đối tượng đó càng nhanh càng tốt. Mức độ mà các đề xuất đó có hiệu quả được xác định theo thực hiện.

&Toán tử đơn nguyên có thể không được áp dụng cho một đối tượng được xác định với registerregister không được sử dụng trong khai báo bên ngoài.

Có một vài quy tắc khác (khá tối nghĩa) dành riêng cho registercác đối tượng đủ tiêu chuẩn:

  • Xác định một đối tượng mảng registercó hành vi không xác định.
    Sửa lỗi: Việc xác định một đối tượng mảng bằngregister , nhưng bạn không thể làm bất cứ điều gì hữu ích với một đối tượng như vậy (lập chỉ mục vào một mảng yêu cầu lấy địa chỉ của phần tử ban đầu).
  • Các _Alignas xác định (mới trong C11) có thể không được áp dụng cho một đối tượng như vậy.
  • Nếu tên tham số được truyền cho va_startmacro được định mức register, hành vi không được xác định.

Có thể có một vài người khác; tải về bản nháp của tiêu chuẩn và tìm kiếm "đăng ký" nếu bạn quan tâm.

Như tên của nó, ý nghĩa ban đầu của nó registerlà yêu cầu một đối tượng được lưu trữ trong một thanh ghi CPU. Nhưng với những cải tiến trong việc tối ưu hóa trình biên dịch, điều này đã trở nên ít hữu ích hơn. Các phiên bản hiện đại của tiêu chuẩn C không đề cập đến các thanh ghi CPU, vì chúng không còn (cần phải) cho rằng có một thứ như vậy (có những kiến ​​trúc không sử dụng các thanh ghi). Sự khôn ngoan phổ biến là việc áp dụng registercho một khai báo đối tượng có nhiều khả năng làm xấu đi mã được tạo, bởi vì nó can thiệp vào phân bổ đăng ký riêng của nhà biên dịch. Vẫn có thể có một vài trường hợp hữu ích (giả sử, nếu bạn thực sự biết mức độ thường xuyên truy cập của một biến và kiến ​​thức của bạn tốt hơn những gì trình biên dịch tối ưu hóa hiện đại có thể tìm ra).

Tác dụng hữu hình chính của registernó là nó ngăn chặn mọi nỗ lực lấy địa chỉ của một đối tượng. Điều này không đặc biệt hữu ích như một gợi ý tối ưu hóa, vì nó chỉ có thể được áp dụng cho các biến cục bộ và trình biên dịch tối ưu hóa có thể tự thấy rằng địa chỉ của một đối tượng như vậy không được sử dụng.


Vì vậy, hành vi của chương trình này thực sự không được xác định theo tiêu chuẩn C? Nó có được định nghĩa tốt trong C ++ không? Tôi nghĩ rằng nó được xác định rõ trong C ++.
Kẻ hủy diệt

@Destructor: Tại sao nó không được xác định? Không có registerđối tượng mảng đủ tiêu chuẩn, nếu đó là những gì bạn đang nghĩ.
Keith Thompson

Ôi xin lỗi tôi đã quên viết từ khóa đăng ký trong khai báo mảng trong hàm main (). Nó có được định nghĩa tốt trong C ++ không?
Kẻ hủy diệt

Tôi đã sai về việc xác định registercác đối tượng mảng; xem điểm đầu tiên được cập nhật trong câu trả lời của tôi. Việc xác định một đối tượng như vậy là hợp pháp, nhưng bạn không thể làm gì với nó. Nếu bạn thêm registervào định nghĩa strong ví dụ của mình , chương trình là bất hợp pháp (vi phạm ràng buộc) trong C. C ++ không đặt ra các hạn chế tương tự register, vì vậy chương trình sẽ là C ++ hợp lệ (nhưng việc sử dụng registersẽ là vô nghĩa).
Keith Thompson

@KeithThndry: registerTừ khóa có thể phục vụ mục đích hữu ích nếu việc lấy địa chỉ của biến đó là hợp pháp nhưng chỉ trong trường hợp ngữ nghĩa sẽ không bị ảnh hưởng bằng cách sao chép biến thành tạm thời khi lấy địa chỉ tạm thời và tải lại từ tạm thời tại điểm tiếp theo. Điều đó sẽ cho phép các trình biên dịch giả định rằng biến có thể được lưu giữ an toàn trong một thanh ghi trên tất cả các truy cập con trỏ với điều kiện là nó bị xóa ở bất kỳ vị trí nào mà địa chỉ của nó được lấy.
supercat

9

Giờ kể chuyện!

C, như một ngôn ngữ, là một sự trừu tượng của một máy tính. Nó cho phép bạn làm mọi thứ, về mặt máy tính làm gì, đó là thao tác bộ nhớ, làm toán, in mọi thứ, v.v.

Nhưng C chỉ là một sự trừu tượng. Và cuối cùng, những gì nó trích ra từ bạn là ngôn ngữ hội. Hội là ngôn ngữ mà CPU đọc và nếu bạn sử dụng nó, bạn sẽ làm mọi thứ theo CPU. CPU làm gì? Về cơ bản, nó đọc từ bộ nhớ, làm toán và ghi vào bộ nhớ. CPU không chỉ làm toán trên các số trong bộ nhớ. Đầu tiên, bạn phải di chuyển một số từ bộ nhớ sang bộ nhớ bên trong CPU được gọi là thanh ghi. Khi bạn đã hoàn thành bất cứ điều gì bạn cần làm với con số này, bạn có thể chuyển nó trở lại bộ nhớ hệ thống bình thường. Tại sao lại sử dụng bộ nhớ hệ thống? Đăng ký bị giới hạn về số lượng. Bạn chỉ nhận được khoảng một trăm byte trong các bộ xử lý hiện đại và các bộ xử lý phổ biến cũ thậm chí còn bị giới hạn tuyệt vời hơn (6502 có 3 thanh ghi 8 bit để bạn sử dụng miễn phí). Vì vậy, phép toán trung bình của bạn trông như sau:

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

Rất nhiều trong số đó là ... không phải toán học. Những hoạt động tải và lưu trữ có thể mất đến một nửa thời gian xử lý của bạn. C, là một sự trừu tượng của máy tính, giải phóng cho lập trình viên nỗi lo về việc sử dụng và tung hứng các thanh ghi, và vì số lượng và loại khác nhau giữa các máy tính, C đặt trách nhiệm phân bổ đăng ký chỉ trên trình biên dịch. Với một ngoại lệ.

Khi bạn khai báo một biến register, bạn đang nói với trình biên dịch "Yo, tôi dự định biến này sẽ được sử dụng rất nhiều và / hoặc tồn tại trong thời gian ngắn. Nếu tôi là bạn, tôi sẽ cố gắng giữ nó trong sổ đăng ký." Khi tiêu chuẩn C nói rằng trình biên dịch không thực sự phải làm bất cứ điều gì, đó là vì tiêu chuẩn C không biết bạn đang biên dịch máy tính nào và nó có thể giống như 6502 ở trên, nơi cần cả 3 thanh ghi chỉ để hoạt động và không có đăng ký dự phòng để giữ số của bạn. Tuy nhiên, khi nó nói rằng bạn không thể lấy địa chỉ, đó là vì các thanh ghi không có địa chỉ. Chúng là tay của bộ xử lý. Vì trình biên dịch không phải cung cấp cho bạn một địa chỉ và vì nó không bao giờ có địa chỉ, nên một số tối ưu hóa hiện đang mở cho trình biên dịch. Nó có thể, nói, giữ số trong một đăng ký luôn. Nó không' Không phải lo lắng về nơi nó được lưu trữ trong bộ nhớ máy tính (ngoài việc cần phải lấy lại nó). Nó thậm chí có thể trừng phạt nó thành một biến khác, đưa nó cho một bộ xử lý khác, cho nó một vị trí thay đổi, v.v.

tl; dr: Các biến có thời gian ngắn làm nhiều phép toán. Đừng khai báo quá nhiều cùng một lúc.


5

Bạn đang rối tung với thuật toán tô màu đồ thị tinh vi của nhà biên dịch. Điều này được sử dụng để phân bổ đăng ký. Vâng, chủ yếu. Nó hoạt động như một gợi ý cho trình biên dịch - đó là sự thật. Nhưng không được bỏ qua toàn bộ vì bạn không được phép lấy địa chỉ của một biến đăng ký (hãy nhớ trình biên dịch, bây giờ vì lòng thương xót của bạn, sẽ cố gắng hành động khác nhau). Mà theo một cách nào đó là nói với bạn không sử dụng nó.

Các từ khóa đã được sử dụng lâu dài, trở lại dài. Khi chỉ có rất ít thanh ghi có thể đếm tất cả bằng ngón tay trỏ của bạn.

Nhưng, như tôi đã nói, không dùng nữa không có nghĩa là bạn không thể sử dụng nó.


13
Một số phần cứng cũ có nhiều thanh ghi hơn các máy Intel hiện đại. Số lượng đăng ký không liên quan gì đến tuổi tác và mọi thứ liên quan đến kiến ​​trúc CPU.
CHỈ CẦN HOẠT ĐỘNG CỦA TÔI NGÀY

2
@JUSTMYc CorrOPINION Thật vậy, X86 về cơ bản có tất cả sáu chiếc, để lại nhiều nhất là 1 hoặc 2 cho sự cống hiến cho 'đăng ký'. Trên thực tế, vì rất nhiều mã đã được viết hoặc chuyển đến một máy kém đăng ký, tôi nghi ngờ điều này đã đóng góp rất lớn cho từ khóa 'đăng ký' trở thành giả dược - không có điểm nào trong việc đăng ký gợi ý khi không có. Ở đây chúng tôi đã hơn 4 năm sau và rất may x86_64 đã tăng nó lên 14 và ARM cũng là một điều lớn.
greggo

4

Chỉ là một bản demo nhỏ (không có mục đích trong thế giới thực) để so sánh: khi xóa registertừ khóa trước mỗi biến, đoạn mã này mất 3,41 giây trên i7 (GCC) của tôi, với register cùng một mã hoàn thành trong 0,7 giây.

#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
Với gcc 4.8.4 và -O3, tôi không có sự khác biệt. Nếu không có -O3 và 40000 lần lặp, tôi có thể giảm ít hơn 50 ms trên tổng thời gian 1,5 giây, nhưng tôi đã không chạy đủ thời gian để biết liệu điều đó có ý nghĩa thống kê hay không.
zstewart

Không có sự khác biệt với CLANG 5.0, nền tảng là AMD64. (Tôi đã kiểm tra đầu ra ASM.)
ern0

4

Tôi đã kiểm tra từ khóa đăng ký theo QNX 6.5.0 bằng mã sau:

#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;
}

Tôi đã nhận được kết quả như sau:

-> 807679611 đã trôi qua

-> Hệ thống này có 3300830000 chu kỳ mỗi giây

-> Chu kỳ tính bằng giây là ~ 0,244600

Và bây giờ không có đăng ký int:

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

Tôi đã nhận:

-> 1421694077 chu kỳ trôi qua

-> Hệ thống này có 3300830000 chu kỳ mỗi giây

-> Chu kỳ tính bằng giây là ~ 0,430700


2

Đăng ký sẽ thông báo cho trình biên dịch rằng bộ mã hóa tin rằng biến này sẽ được ghi / đọc đủ để chứng minh lưu trữ của nó trong một trong số ít các thanh ghi có sẵn để sử dụng biến. Đọc / ghi từ các thanh ghi thường nhanh hơn và có thể yêu cầu một bộ mã op nhỏ hơn.

Ngày nay, điều này không hữu ích lắm, vì hầu hết các trình tối ưu hóa của trình biên dịch tốt hơn bạn trong việc xác định liệu một thanh ghi có nên được sử dụng cho biến đó hay không và trong bao lâu.


2

Trong những năm bảy mươi, vào đầu ngôn ngữ C, từ khóa đăng ký đã được giới thiệu để cho phép lập trình viên đưa ra gợi ý cho trình biên dịch, nói với nó rằng biến đó sẽ được sử dụng rất thường xuyên và nên khôn ngoan giữ giá trị của nó trong một trong các thanh ghi nội bộ của bộ xử lý.

Ngày nay, trình tối ưu hóa hiệu quả hơn nhiều so với lập trình viên để xác định các biến có nhiều khả năng được lưu giữ trong các thanh ghi và trình tối ưu hóa không phải lúc nào cũng đưa ra gợi ý của lập trình viên.

Vì vậy, nhiều người sai khuyến cáo không sử dụng từ khóa đăng ký.

Hãy xem tại sao!

Từ khóa đăng ký có tác dụng phụ liên quan: bạn không thể tham chiếu (lấy địa chỉ của) một biến loại đăng ký.

Mọi người khuyên người khác không nên sử dụng các thanh ghi nhận sai điều này như là một đối số bổ sung.

Tuy nhiên, thực tế đơn giản là bạn biết rằng bạn không thể lấy địa chỉ của biến đăng ký, cho phép trình biên dịch (và trình tối ưu hóa của nó) biết rằng giá trị của biến này không thể được sửa đổi gián tiếp thông qua một con trỏ.

Khi tại một điểm nhất định của luồng lệnh, biến đăng ký có giá trị được gán trong thanh ghi của bộ xử lý và thanh ghi không được sử dụng kể từ khi lấy giá trị của biến khác, trình biên dịch biết rằng nó không cần tải lại giá trị của biến trong thanh ghi đó. Điều này cho phép tránh truy cập bộ nhớ vô dụng đắt tiền.

Làm các bài kiểm tra của riêng bạn và bạn sẽ nhận được những cải tiến hiệu suất đáng kể trong các vòng lặp bên trong nhất của bạn.

c_register_side_effect_performance_boost


1

Trên các trình biên dịch C được hỗ trợ, nó cố gắng tối ưu hóa mã để giá trị của biến được giữ trong một thanh ghi bộ xử lý thực tế.


1

Trình biên dịch Visual C ++ của Microsoft bỏ qua registertừ khóa khi tối ưu hóa phân bổ đăng ký toàn cầu (cờ trình biên dịch / Oe) được bật.

Xem đăng ký từ khóa trên MSDN.


1

Đăng ký từ khóa báo cho trình biên dịch lưu trữ biến cụ thể trong các thanh ghi CPU để có thể truy cập nhanh. Từ quan điểm của một lập trình viên, từ khóa đăng ký được sử dụng cho các biến được sử dụng nhiều trong một chương trình, để trình biên dịch có thể tăng tốc mã. Mặc dù nó phụ thuộc vào trình biên dịch nên giữ biến trong các thanh ghi CPU hoặc bộ nhớ chính.


0

Đăng ký chỉ ra trình biên dịch để tối ưu hóa mã này bằng cách lưu trữ biến cụ thể đó trong các thanh ghi sau đó trong bộ nhớ. nó là một yêu cầu cho trình biên dịch, trình biên dịch có thể hoặc không thể xem xét yêu cầu này. Bạn có thể sử dụng cơ sở này trong trường hợp một số biến của bạn đang được truy cập rất thường xuyên. Ví dụ: Một vòng lặp.

Một điều nữa là nếu bạn khai báo một biến là đăng ký thì bạn không thể lấy địa chỉ của nó vì nó không được lưu trong bộ nhớ. nó được phân bổ trong thanh ghi CPU.


0

gcc 9.3 đầu ra asm, không sử dụng cờ tối ưu hóa (mọi thứ trong câu trả lời này đều đề cập đến việc biên dịch tiêu chuẩn mà không cần cờ tối ưu hóa):

#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

Lực lượng này ebxđược sử dụng để tính toán, có nghĩa là nó cần được đẩy lên ngăn xếp và khôi phục ở cuối hàm vì nó được lưu lại.registertạo ra nhiều dòng mã hơn và 1 bộ nhớ ghi và 1 bộ nhớ đọc (mặc dù thực tế, điều này có thể đã được tối ưu hóa thành 0 R / W nếu việc tính toán được thực hiện esi, đó là những gì xảy ra khi sử dụng C ++ const register). Không sử dụng registernguyên nhân 2 ghi và 1 đọc (mặc dù lưu trữ để tải chuyển tiếp sẽ xảy ra trên đọc). Điều này là do giá trị phải được trình bày và cập nhật trực tiếp trên ngăn xếp để có thể đọc giá trị chính xác theo địa chỉ (con trỏ). registerkhông có yêu cầu này và không thể được chỉ ra. constregistervề cơ bản là trái ngược vớivolatile và sử dụngvolatilesẽ ghi đè tối ưu hóa const ở tập tin và phạm vi khối và registertối ưu hóa ở phạm vi khối. const registerregistersẽ tạo ra các đầu ra giống hệt nhau vì const không làm gì trên C ở phạm vi khối, do đó chỉ registeráp dụng tối ưu hóa.

Trên clang, registerđược bỏ qua nhưng consttối ưu hóa vẫn xảy ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.