Tại sao một bộ xử lý có 32 thanh ghi?


52

Tôi đã luôn tự hỏi tại sao bộ xử lý dừng lại ở 32 thanh ghi. Đây là phần nhanh nhất của máy, tại sao không tạo ra bộ xử lý lớn hơn với nhiều thanh ghi hơn? Điều đó có nghĩa là sẽ ít đi RAM hơn?


2
Tôi đoán vượt quá một điểm nhất định tất cả các biến cục bộ của bạn phù hợp với các thanh ghi. Dữ liệu thực tế mà bạn đang làm việc có lẽ quá lớn
Niklas B.

14
Giảm dần lợi nhuận. Rõ ràng, các thanh ghi "đắt hơn" (theo nhiều nghĩa khác nhau) so với RAM hoặc chúng ta chỉ có 8GB thanh ghi.
David Richerby

5
Một trong những lý do của nó rất nhanh là vì không có nhiều trong số họ.
stackErr

5
Có một sự khác biệt giữa tổng số bao nhiêu thanh ghi cpu và tổng số lượng bạn có thể sử dụng cùng một lúc.
Thorbjørn Ravn Andersen

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

Câu trả lời:


82

Đầu tiên, không phải tất cả các kiến ​​trúc bộ xử lý dừng lại ở 32 thanh ghi. Hầu như tất cả các kiến ​​trúc RISC có 32 thanh ghi được hiển thị trong tập lệnh thực sự có 32 thanh ghi nguyên và thêm 32 thanh ghi dấu phẩy động (vì vậy 64). (Dấu phẩy động "thêm" sử dụng các thanh ghi khác với số nguyên "thêm".) Kiến trúc SPARC có các cửa sổ đăng ký. Trên SPARC, bạn chỉ có thể truy cập 32 thanh ghi số nguyên cùng một lúc, nhưng các thanh ghi hoạt động như một ngăn xếp và bạn có thể đẩy và bật các thanh ghi mới 16 tại một thời điểm. Kiến trúc Itanium của HP / Intel có 128 số nguyên và 128 thanh ghi dấu phẩy động được hiển thị trong tập lệnh. Các GPU hiện đại của NVidia, AMD, Intel, ARM và Imagination Technologies, tất cả đều phơi bày số lượng lớn các thanh ghi trong các tệp đăng ký của chúng. (Tôi biết điều này đúng với kiến ​​trúc của NVidia và Intel, tôi không rành lắm về các tập lệnh AMD, ARM và Tưởng tượng, nhưng tôi nghĩ rằng các tệp đăng ký cũng lớn ở đó.)

Thứ hai, hầu hết các bộ vi xử lý hiện đại đều thực hiện đổi tên thanh ghi để loại bỏ việc xê-ri hóa không cần thiết do cần sử dụng lại tài nguyên, vì vậy các tệp thanh ghi vật lý cơ bản có thể lớn hơn (các thanh ghi 96, 128 hoặc 192 trên một số máy.) Điều này (và lập lịch trình động) loại bỏ một số cần cho trình biên dịch để tạo ra rất nhiều tên đăng ký duy nhất, trong khi vẫn cung cấp một tệp đăng ký lớn hơn cho bộ lập lịch.

Có hai lý do tại sao có thể khó tăng thêm số lượng thanh ghi được hiển thị trong tập lệnh. Trước tiên, bạn cần có khả năng chỉ định các định danh đăng ký trong mỗi hướng dẫn. 32 thanh ghi yêu cầu một bộ xác định thanh ghi 5 bit, vì vậy các lệnh 3 địa chỉ (phổ biến trên các kiến ​​trúc RISC) dành 15 trong số 32 bit lệnh chỉ để chỉ định các thanh ghi. Nếu bạn tăng nó lên 6 hoặc 7 bit, thì bạn sẽ có ít không gian hơn để chỉ định opcodes và hằng. GPU và Itanium có hướng dẫn lớn hơn nhiều . Các hướng dẫn lớn hơn có chi phí: bạn cần sử dụng nhiều bộ nhớ lệnh hơn, do đó hành vi bộ đệm hướng dẫn của bạn ít lý tưởng hơn.

Lý do thứ hai là thời gian truy cập. Bạn càng tạo ra bộ nhớ càng lớn thì việc truy cập dữ liệu từ nó càng chậm. (Chỉ xét về mặt vật lý cơ bản: dữ liệu được lưu trữ trong không gian 2 chiều, vì vậy nếu bạn đang lưu trữ bit, khoảng cách trung bình đến một bit cụ thể là .) Tệp đăng ký chỉ là một bộ nhớ đa cổng nhỏ và một trong những hạn chế của việc làm cho nó lớn hơn là cuối cùng bạn sẽ cần bắt đầu đồng hồ máy của mình chậm hơn để chứa tệp đăng ký lớn hơn. Thông thường về tổng hiệu suất thì đây là một mất mát. O ( nO(n)


1
Tôi đã đề cập đến 256 FPR của SPARC64 VIIIfx và 32 GPR ngoài cửa sổ bổ sung, được thực hiện bằng cách thêm một lệnh Set XAR cung cấp 13 bit cho mỗi một hoặc hai lệnh tiếp theo. Nó đã được nhắm mục tiêu tại HPC, vì vậy số lượng đăng ký là dễ hiểu hơn. Tôi cũng đã bị cám dỗ để giải thích một số sự đánh đổi và kỹ thuật liên quan đến nhiều đăng ký hơn; nhưng bạn đã cho thấy sự khôn ngoan để tránh một câu trả lời mệt mỏi hơn (và thậm chí sau đó không đầy đủ).
Paul A. Clayton

2
Thêm một chút về lợi ích giảm dần của nhiều thanh ghi hơn cho mã "mục đích chung" có thể đáng giá, mặc dù việc tìm kiếm các phép đo có ý nghĩa là không dễ dàng. Tôi nghĩ Mitch Alsup đã đề cập trên comp.arch rằng việc mở rộng x86 lên 32 thanh ghi thay vì 16 sẽ tăng khoảng 3% hiệu suất so với (ISTR) 10-15% cho phần mở rộng đăng ký 8 đến 16 đã được chọn. Ngay cả đối với một ISA lưu trữ tải, đến 64 có thể cung cấp rất ít lợi ích (ít nhất là cho mã GP hiện tại). (BTW, GPU thường chia sẻ các thanh ghi trên các luồng: ví dụ: một luồng với 250 để lại trên 16 tổng riêng tư cho các luồng khác.)
Paul A. Clayton

Thật thú vị khi thấy rằng quản lý môi trường (do đó chuyển đổi alpha), thường được liên kết với các ngôn ngữ cấp cao, thực sự được sử dụng ở cấp độ đăng ký.
babou

@ PaulA.Clayton Tôi luôn nghĩ rằng IA-64 là kiến ​​trúc có số lượng thanh ghi ISA lớn nhất
phuclv

@ LưuViênPhúc SPARC64 VIIIfx là đặc trưng của HPC. FYI, Am29k (được giới thiệu vào khoảng 1987-8 ) có 64 GPR toàn cầu và 128 cửa sổ có nhiều GPR hơn Itanium (có 8 thanh ghi chi nhánh và một thanh ghi đếm vòng lặp có chức năng là GPR trong một số ISAs khác).
Paul A. Clayton

16

Chỉ hai lý do nữa để giới hạn số lượng đăng ký:

  • Ít được mong đợi: CPU như các mẫu Intel / AMD x64 hiện tại có 32kByte và nhiều bộ đệm L1-D hơn và việc truy cập vào bộ đệm L1 thường chỉ mất một chu kỳ xung nhịp (so với khoảng một trăm chu kỳ xung nhịp cho một RAM hoàn chỉnh truy cập). Vì vậy, có rất ít để có được từ việc có nhiều dữ liệu trong các thanh ghi so với việc có dữ liệu trong bộ đệm L1
  • Chi phí tính toán bổ sung: Việc có nhiều thanh ghi tạo ra một chi phí thực sự có thể khiến máy tính chạy chậm hơn:
    • Trong môi trường đa nhiệm, một bộ chuyển đổi tác vụ thường phải lưu nội dung của tất cả các thanh ghi của quá trình còn lại vào bộ nhớ và phải tải những quy trình cần nhập. Bạn càng đăng ký nhiều, việc này càng mất nhiều thời gian.
    • Tương tự, trong các kiến ​​trúc không có cửa sổ đăng ký, các lệnh gọi hàm xếp tầng sử dụng cùng một bộ các thanh ghi. Vì vậy, một hàm A gọi một hàm B sử dụng cùng một bộ các thanh ghi như chính B. Do đó, B phải lưu nội dung của tất cả các thanh ghi mà nó sử dụng (vẫn giữ các giá trị của A) và phải ghi lại chúng trước khi quay lại (trong một số quy ước gọi, đó là công việc của A để lưu nội dung đăng ký của nó trước khi gọi B, nhưng trên cao là tương tự). Bạn càng có nhiều thanh ghi, việc tiết kiệm này càng mất nhiều thời gian và do đó, một cuộc gọi hàm trở nên đắt hơn.

Làm thế nào nó hoạt động cho bộ đệm L1 để chúng ta không gặp vấn đề tương tự như đối với các thanh ghi?
babou

4
Trên các bộ xử lý hiệu suất cao, độ trễ L1 Dcache thường là 3 hoặc 4 chu kỳ (bao gồm cả việc tạo địa chỉ), ví dụ, Haswell của Intel có độ trễ 4 chu kỳ (không có độ trễ đăng ký phụ thuộc dữ liệu cũng dễ dàng ẩn trong đường ống hơn). Dcache cũng có xu hướng hỗ trợ ít lượt truy cập hơn trong mỗi chu kỳ (ví dụ: 2 đọc, 1 ghi cho Haswell) so với tệp đăng ký (ví dụ: 4 đọc, 6 ghi cho Alpha 21264 sao chép tệp, 2 tệp có 4 lần đọc nhanh hơn 1 với số 8).
Paul A. Clayton

@ PaulA.Clayton: Nếu bộ đệm L1 có độ trễ 3-4 chu kỳ, điều đó sẽ gợi ý rằng có thể có một số lợi ích khi có một vài bộ 64 từ bộ nhớ một chu kỳ với không gian địa chỉ 64 từ của riêng nó và các hướng dẫn "tải / lưu trữ trực tiếp" chuyên dụng, đặc biệt là nếu có một cách để đẩy tất cả các giá trị khác không, theo sau là một từ cho biết các từ đó khác không, và sau đó là một cách để bật lại chúng (bỏ qua mọi thanh ghi không được bật) . Nhiều phương thức có từ 16 đến 60 từ biến cục bộ, do đó việc cắt giảm thời gian truy cập cho những người từ 3-4 chu kỳ có vẻ hữu ích.
supercat

@supercat Các ý tưởng bộ đệm khác nhau (và toàn cầu / TLS [ví dụ: Knapsack]) đã được trình bày trong các bài báo học thuật cũng như các cơ chế như bộ đệm chữ ký ( PDF ) Sử dụng thực tế, không quá nhiều (có vẻ như vậy). Điều này đang trở nên sôi nổi (vì vậy có lẽ nên kết thúc hoặc đi nơi khác).
Paul A. Clayton

4

Rất nhiều mã có nhiều truy cập bộ nhớ (30% là một con số điển hình). Trong số đó, thông thường khoảng 2/3 là truy cập đọc và 1/3 là truy cập ghi. Điều này không phải là do hết đăng ký nhiều như truy cập mảng, truy cập các biến thành viên đối tượng, v.v.

Điều này phải được thực hiện trong bộ nhớ (hoặc bộ đệm dữ liệu) do cách tạo C / C ++ (mọi thứ bạn có thể nhận được một con trỏ cần phải có một địa chỉ phải được lưu trong bộ nhớ). Nếu trình biên dịch có thể đoán rằng bạn sẽ không ghi vào các biến willy-nilly bằng các thủ thuật con trỏ gián tiếp điên rồ, nó sẽ đưa chúng vào các thanh ghi, và điều này hoạt động rất tốt cho các biến chức năng nhưng không phải cho các biến có thể truy cập toàn cầu (nói chung, mọi thứ xuất phát từ malloc ()) bởi vì về cơ bản không thể đoán được trạng thái toàn cầu sẽ thay đổi như thế nào.

Bởi vì điều này, không phổ biến rằng trình biên dịch sẽ có thể làm bất cứ điều gì với hơn 16 thanh ghi sử dụng chung dù sao đi nữa. Đó là lý do tại sao tất cả các kiến ​​trúc phổ biến có nhiều về điều đó (ARM có 16).

MIPS và các RISC khác có xu hướng có 32 vì không khó để có nhiều đăng ký - chi phí đủ thấp nên có một chút "tại sao không?". Hơn 32 phần lớn là vô dụng và có nhược điểm là làm cho tệp đăng ký dài hơn để truy cập (mỗi lần nhân đôi số lượng thanh ghi có khả năng thêm một lớp ghép kênh bổ sung thêm một chút chậm trễ ...). Nó cũng làm cho các lệnh trung bình dài hơn một chút - có nghĩa là khi chạy loại chương trình phụ thuộc vào băng thông bộ nhớ lệnh, các thanh ghi bổ sung của bạn thực sự làm bạn chậm lại!

Nếu cpu của bạn theo thứ tự và không đăng ký đổi tên và bạn đang cố gắng thực hiện nhiều thao tác trên mỗi chu kỳ (hơn 3), thì theo lý thuyết, bạn cần nhiều đăng ký hơn khi số lượng op của mỗi chu kỳ tăng lên. Đây là lý do tại sao Itanium có rất nhiều đăng ký! Nhưng trong thực tế, ngoài mã định hướng số nổi hoặc SIMD (mà Itanium thực sự giỏi), hầu hết các mã sẽ có rất nhiều bộ nhớ đọc / ghi và nhảy khiến cho giấc mơ này có nhiều hơn 3 op mỗi chu kỳ (đặc biệt là trong các phần mềm hướng máy chủ như cơ sở dữ liệu, trình biên dịch, thực thi ngôn ngữ cấp cao như javascript, mô phỏng, v.v.). Đây là những gì đã đánh chìm Itanium.

Tất cả là do sự khác biệt giữa tính toán và thực thi!


2

Ai nói với bạn rằng bộ xử lý luôn có 32 thanh ghi? x86 có 8, ARM 32-bit và x86_64 có 16, IA-64 có 128 và nhiều số khác. Bạn có thể có một cái nhìn ở đây . Ngay cả MIPS, PPC hoặc bất kỳ kiến ​​trúc nào có 32 thanh ghi mục đích chung trong tập lệnh, số này lớn hơn 32 vì vẫn luôn có các thanh ghi cờ (nếu có), các thanh ghi điều khiển ... không bao gồm các thanh ghi được đổi tên và các thanh ghi phần cứng

Mọi thứ đều có giá của nó. Số lượng thanh ghi càng lớn, bạn càng có nhiều công việc khi thực hiện chuyển đổi tác vụ, bạn càng cần nhiều không gian hơn trong mã hóa lệnh. Nếu bạn có ít đăng ký hơn, bạn không phải lưu trữ và khôi phục nhiều khi gọi và trả lại từ các chức năng hoặc chuyển đổi các tác vụ với việc không có các thanh ghi trong một số mã mở rộng tính toán

Hơn nữa, tập tin đăng ký càng lớn, nó sẽ càng tốn kém và phức tạp. SRAM là RAM nhanh nhất và đắt nhất vì vậy nó chỉ được sử dụng trong bộ đệm CPU. Nhưng nó vẫn rẻ hơn nhiều và chiếm ít diện tích hơn một tệp đăng ký có cùng dung lượng.


2

Ví dụ, bộ xử lý Intel điển hình có "chính thức" 16 số nguyên và 16 thanh ghi vector. Nhưng trong thực tế, có nhiều hơn nữa: Bộ xử lý sử dụng "đăng ký đổi tên". Nếu bạn có một lệnh reg3 = reg1 + reg2, bạn sẽ gặp vấn đề nếu một lệnh khác sử dụng reg3 chưa kết thúc - bạn không thể thực hiện lệnh mới trong trường hợp nó ghi đè lên reg3 trước khi nó được đọc bởi lệnh trước đó.

Do đó, có khoảng 160 hoặc hơn đăng ký thực sự . Vì vậy, hướng dẫn đơn giản ở trên được thay đổi thành "regX = reg1 + reg2 và hãy nhớ rằng regX chứa reg3". Nếu không đổi tên các thanh ghi, việc thực hiện ngoài trật tự sẽ hoàn toàn chết trong nước.


1

Tôi không phải là kỹ sư điện, nhưng tôi nghĩ một khả năng khác vì lý do giới hạn số lượng thanh ghi, là định tuyến. Có một số lượng hạn chế các đơn vị số học, và chúng phải có thể lấy đầu vào từ mỗi thanh ghi, và đầu ra cho mỗi thanh ghi. Điều này đặc biệt đúng khi bạn có các chương trình đường ống có thể thực hiện nhiều lệnh trong mỗi chu kỳ.

Một phiên bản đơn giản này sẽ có độ phức tạp , làm tăng số lượng thanh ghi không thể quét được, hoặc nếu không yêu cầu thiết kế lại định tuyến đến một thứ gì đó phức tạp hơn nhiều để định tuyến mọi thứ với độ phức tạp tốt hơn.O(n2)

Tôi có ý tưởng cho câu trả lời này khi xem một số cuộc nói chuyện của Ivan Godard trên CPU Mill. Một phần của sự đổi mới của CPU Mill là bạn không thể xuất ra các thanh ghi tùy ý - tất cả các đầu ra được đẩy vào ngăn xếp thanh ghi hoặc "vành đai", do đó làm giảm các vấn đề định tuyến, bởi vì bạn luôn biết đầu ra sẽ đi đâu. Lưu ý rằng họ vẫn có vấn đề định tuyến để nhận các thanh ghi đầu vào cho các đơn vị số học.

Xem Kiến trúc CPU Mill - Vành đai (2 trên 9) để biết báo cáo sự cố và giải pháp của Mill.


"Họ phải có khả năng nhận đầu vào từ mọi đăng ký, và đầu ra cho mọi đăng ký." - Tôi hy vọng điều này thường được thực hiện với một chiếc xe buýt, không cần phải có một kết nối riêng với ALU (s) cho mỗi đăng ký.
dùng253751

1
@immibis: Nếu bạn muốn di chuyển dữ liệu trong 300 picosecond thì xe buýt sẽ không làm điều đó. Và nếu bạn muốn di chuyển nhiều dữ liệu xung quanh (ví dụ để thực hiện ba hướng dẫn với hai toán hạng và một kết quả trong cùng một chu kỳ), một chiếc xe buýt sẽ hoàn toàn không hoạt động.
gnasher729

0

Đối với MIPS ISA, Hennessy và Patterson, Tổ chức máy tính và Thiết kế phiên bản thứ 4 p. 176, trả lời trực tiếp câu hỏi cụ thể này:

Nhỏ hơn là nhanh hơn. Mong muốn về tốc độ là lý do mà MIPS có 32 thanh ghi chứ không phải nhiều hơn nữa.

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.