Nếu số lượng đăng ký quá nhanh, tại sao chúng ta không có thêm chúng?


88

Trong 32bit, chúng tôi có 8 thanh ghi "mục đích chung". Với 64bit, số lượng tăng gấp đôi, nhưng nó dường như không phụ thuộc vào chính sự thay đổi 64bit.
Bây giờ, nếu các thanh ghi quá nhanh (không có quyền truy cập bộ nhớ), tại sao không có nhiều trong số chúng một cách tự nhiên? Các nhà xây dựng CPU không nên làm việc càng nhiều thanh ghi vào CPU càng tốt? Hạn chế hợp lý tại sao chúng ta chỉ có số tiền chúng ta có?


CPU và GPU ẩn độ trễ chủ yếu bằng bộ nhớ đệm và đa luồng lớn tương ứng. Vì vậy, CPU có (hoặc cần) ít thanh ghi, trong khi GPU có hàng chục nghìn thanh ghi. Xem bài khảo sát của tôi trên tệp đăng ký GPU thảo luận về tất cả các yếu tố và sự đánh đổi này.
user984260

Câu trả lời:


119

Có nhiều lý do khiến bạn không chỉ có một số lượng lớn các đăng ký:

  • Chúng được liên kết chặt chẽ với hầu hết các giai đoạn đường ống. Đối với người mới bắt đầu, bạn cần theo dõi thời gian tồn tại của chúng và chuyển tiếp kết quả trở lại các giai đoạn trước đó. Sự phức tạp trở nên khó chữa rất nhanh và số lượng dây (theo nghĩa đen) liên quan tăng lên với tốc độ tương tự. Nó đắt về diện tích, điều đó cuối cùng có nghĩa là nó đắt về công suất, giá cả và hiệu suất sau một thời điểm nhất định.
  • Nó chiếm không gian mã hóa hướng dẫn. 16 thanh ghi chiếm 4 bit cho nguồn và đích, và 4 thanh khác nếu bạn có lệnh 3 toán hạng (ví dụ: ARM). Đó là rất nhiều không gian mã hóa tập lệnh được sử dụng chỉ để chỉ định thanh ghi. Điều này cuối cùng ảnh hưởng đến giải mã, kích thước mã và một lần nữa độ phức tạp.
  • Có nhiều cách tốt hơn để đạt được cùng một kết quả ...

Ngày nay, chúng ta thực sự có rất nhiều sổ đăng ký - chúng không được lập trình rõ ràng. Chúng tôi đã "đăng ký đổi tên". Trong khi bạn chỉ truy cập vào một tập hợp nhỏ (8-32 thanh ghi), chúng thực sự được hỗ trợ bởi một tập hợp lớn hơn nhiều (ví dụ: 64-256). Sau đó, CPU theo dõi khả năng hiển thị của từng thanh ghi và phân bổ chúng cho tập hợp đã đổi tên. Ví dụ: bạn có thể tải, sửa đổi, sau đó lưu trữ vào một thanh ghi nhiều lần liên tiếp và mỗi hoạt động này thực sự được thực hiện độc lập tùy thuộc vào các lần bỏ sót bộ nhớ cache, v.v. Trong ARM:

ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]

Các lõi Cortex A9 thực hiện đổi tên thanh ghi, vì vậy lần tải đầu tiên đến "r0" thực sự chuyển đến một thanh ghi ảo đã được đổi tên - hãy gọi nó là "v0". Việc tải, tăng và lưu trữ xảy ra trên "v0". Trong khi đó, chúng tôi cũng thực hiện tải / sửa đổi / lưu trữ thành r0 một lần nữa, nhưng điều đó sẽ được đổi tên thành "v1" vì đây là một chuỗi hoàn toàn độc lập sử dụng r0. Giả sử tải từ con trỏ trong "r4" bị đình trệ do lỗi bộ nhớ cache. Không sao cả - chúng ta không cần đợi "r0" sẵn sàng. Bởi vì nó được đổi tên, chúng tôi có thể chạy chuỗi tiếp theo với "v1" (cũng được ánh xạ đến r0) - và có lẽ đó là một lần truy cập bộ nhớ cache và chúng tôi vừa có một chiến thắng hiệu suất lớn.

ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]

Tôi nghĩ rằng x86 ngày nay có một số lượng khổng lồ các đăng ký được đổi tên (ballpark 256). Điều đó có nghĩa là có 8 bit nhân 2 cho mỗi lệnh chỉ để nói nguồn và đích là gì. Nó sẽ làm tăng số lượng dây cần thiết trên lõi và kích thước của nó. Vì vậy, có một điểm thú vị xung quanh 16-32 thanh ghi mà hầu hết các nhà thiết kế đã giải quyết và đối với các thiết kế CPU không theo thứ tự, đổi tên thanh ghi là cách để giảm thiểu nó.

Chỉnh sửa : Tầm quan trọng của việc thực hiện không theo thứ tự và đăng ký đổi tên về điều này. Khi bạn đã có OOO, số lượng thanh ghi không quan trọng lắm, vì chúng chỉ là "thẻ tạm thời" và được đổi tên thành tập thanh ghi ảo lớn hơn nhiều. Bạn không muốn số lượng quá nhỏ, vì sẽ khó viết các chuỗi mã nhỏ. Đây là một vấn đề đối với x86-32, bởi vì 8 thanh ghi hạn chế có nghĩa là rất nhiều thời gian tạm thời kết thúc qua ngăn xếp và lõi cần thêm logic để chuyển tiếp đọc / ghi vào bộ nhớ. Nếu bạn không có OOO, bạn thường nói về một lõi nhỏ, trong trường hợp đó, một bộ thanh ghi lớn là một lợi ích về chi phí / hiệu suất kém.

Vì vậy, có một điểm ngọt ngào tự nhiên cho kích thước ngân hàng thanh ghi, tối đa là khoảng 32 thanh ghi được tạo ra cho hầu hết các loại CPU. x86-32 có 8 thanh ghi và nó chắc chắn quá nhỏ. ARM đã đi với 16 đăng ký và đó là một sự thỏa hiệp tốt. 32 đăng ký là hơi quá nhiều nếu có - cuối cùng bạn không cần 10 cuối cùng hoặc lâu hơn.

Điều này không liên quan đến các thanh ghi bổ sung mà bạn nhận được cho SSE và các bộ đồng xử lý dấu chấm động vectơ khác. Chúng có ý nghĩa như một tập hợp bổ sung vì chúng chạy độc lập với lõi số nguyên và không làm tăng độ phức tạp của CPU theo cấp số nhân.


12
Câu trả lời tuyệt vời - Tôi muốn đưa ra một lý do khác vào hỗn hợp - càng có nhiều thanh ghi, thì càng mất nhiều thời gian để ném chúng vào / kéo chúng ra khỏi ngăn xếp khi chuyển đổi ngữ cảnh. Chắc chắn không phải là vấn đề lớn, mà là một sự cân nhắc.
Sẽ A

7
@Will Một điểm tốt. Tuy nhiên, các kiến ​​trúc có nhiều thanh ghi có những cách giảm thiểu chi phí này. ABI thường sẽ lưu trữ hầu hết các thanh ghi, vì vậy bạn chỉ phải lưu một tập lõi. Chuyển đổi ngữ cảnh thường đủ đắt để lưu / khôi phục thêm không tốn nhiều so với tất cả các băng đỏ khác. SPARC thực sự hoạt động xung quanh vấn đề này bằng cách biến ngân hàng thanh ghi trở thành một "cửa sổ" trên một vùng bộ nhớ, vì vậy nó sẽ thay đổi tỷ lệ phần nào với điều này (kiểu vẫy tay).
John Ripley

4
Hãy xem xét tâm trí của tôi bị thổi bay bởi một câu trả lời thấu đáo như vậy mà tôi chắc chắn không mong đợi. Ngoài ra, cảm ơn vì lời giải thích đó về lý do tại sao chúng ta không thực sự cần nhiều thanh ghi được đặt tên, điều đó rất thú vị! Tôi thực sự thích thú khi đọc câu trả lời của bạn, bởi vì tôi hoàn toàn quan tâm đến những gì đang diễn ra "dưới mui xe". :) Tôi sẽ đợi thêm một chút trước khi chấp nhận câu trả lời, bởi vì bạn không bao giờ biết, nhưng +1 của tôi là chắc chắn.
Xeo

1
bất kể trách nhiệm lưu sổ đăng ký nằm ở đâu thì thời gian đó là chi phí quản trị. OK vì vậy chuyển đổi ngữ cảnh có thể không phải là trường hợp thường xuyên xảy ra nhất, nhưng ngắt thì có. Các quy trình được mã hóa thủ công có thể tiết kiệm trên các thanh ghi nhưng nếu trình điều khiển được viết bằng C thì rất có thể hàm được khai báo ngắt sẽ lưu từng thanh ghi, gọi isr và sau đó khôi phục tất cả các thanh ghi đã lưu. IA-32 có lợi thế về ngắt với 15-20 regs của nó so với regs 32+ của kiến ​​trúc RISC.
Olof Forshell

1
Câu trả lời tuyệt vời, nhưng tôi không đồng ý với việc so sánh trực tiếp các sổ đăng ký "đã đổi tên" với các sổ đăng ký "thực" có thể nhấn. Trên x86-32, ngay cả với 256 thanh ghi bên trong, bạn không thể sử dụng nhiều hơn 8 giá trị tạm thời được lưu trữ trong thanh ghi trong bất kỳ điểm thực thi nào. Về cơ bản, đổi tên đăng ký chỉ là một sản phẩm phụ gây tò mò của OOE, không có gì hơn.
noop

12

Chúng tôi làm có nhiều của Them

Bởi vì hầu hết mọi lệnh đều phải chọn 1, 2 hoặc 3 thanh ghi có thể nhìn thấy được về mặt kiến ​​trúc, việc mở rộng số lượng của chúng sẽ làm tăng kích thước mã lên vài bit trên mỗi lệnh và do đó làm giảm mật độ mã. Nó cũng làm tăng số lượng ngữ cảnh phải được lưu dưới dạng trạng thái luồng và một phần được lưu trong bản ghi kích hoạt của một hàm . Các hoạt động này xảy ra thường xuyên. Các khóa liên động của đường ống phải kiểm tra bảng điểm cho mọi thanh ghi và điều này có độ phức tạp về thời gian và không gian bậc hai. Và có lẽ lý do lớn nhất chỉ đơn giản là khả năng tương thích với tập lệnh đã được xác định.

Nhưng hóa ra, nhờ đổi tên đăng ký , chúng tôi thực sự có rất nhiều đăng ký và thậm chí chúng tôi không cần lưu chúng. CPU thực sự có nhiều bộ thanh ghi và nó tự động chuyển đổi giữa chúng khi mã của bạn được giải thích. Nó hoàn toàn làm điều này để giúp bạn có thêm nhiều đăng ký.

Thí dụ:

load  r1, a  # x = a
store r1, x
load  r1, b  # y = b
store r1, y

Trong kiến ​​trúc chỉ có r0-r7, mã sau có thể được CPU tự động viết lại như sau:

load  r1, a
store r1, x
load  r10, b
store r10, y

Trong trường hợp này, r10 là một thanh ghi ẩn được thay thế tạm thời cho r1. CPU có thể nói rằng giá trị của r1 không bao giờ được sử dụng lại sau lần lưu trữ đầu tiên. Điều này cho phép lần tải đầu tiên bị trì hoãn (ngay cả một lần truy cập vào bộ nhớ cache trên chip thường mất vài chu kỳ) mà không yêu cầu độ trễ của lần tải thứ hai hoặc lần lưu trữ thứ hai.


2

Họ luôn thêm các thanh ghi, nhưng chúng thường bị ràng buộc với các lệnh mục đích đặc biệt (ví dụ: SIMD, SSE2, v.v.) hoặc yêu cầu biên dịch theo một kiến ​​trúc CPU cụ thể, điều này làm giảm tính di động. Các hướng dẫn hiện tại thường hoạt động trên các thanh ghi cụ thể và không thể tận dụng các thanh ghi khác nếu chúng có sẵn. Tập lệnh kế thừa và tất cả.


1

Để thêm một thông tin thú vị nhỏ ở đây, bạn sẽ nhận thấy rằng việc có 8 thanh ghi có kích thước giống nhau cho phép các mã quang duy trì tính nhất quán với ký hiệu thập lục phân. Ví dụ, hướng dẫn push axlà opcode 0x50 trên x86 và chuyển lên 0x57 cho đăng ký cuối cùng di. Sau đó, lệnh pop axbắt đầu ở 0x58 và đi lên 0x5F pop diđể hoàn thành cơ số 16 đầu tiên. Tính nhất quán hệ thập lục phân được duy trì với 8 thanh ghi trên một kích thước.


2
Trên x86 / 64, tiền tố lệnh REX mở rộng các chỉ số thanh ghi với nhiều bit hơn.
Alexey Frunze
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.