Tìm hiểu về cách làm việc của CPU (Phần cuối)

Sau khi đã tìm hiểu về cấu trúc vật lý của CPU trong nội dung các bài trước, bài này chúng tôi sẽ giới thiệu sơ qua đến bạn đọc cách CPU thực hiện xử lý các chỉ lệnh.
  • Tìm hiểu về cách làm việc của CPU (Phần I) 
  • Tìm hiểu về cách làm việc của CPU (Phần II)

Việc xử lý chỉ lệnh

Khối tìm nạp chịu hoàn toàn trách nhiệm về việc nạp các chỉ lệnh từ bộ nhớ. Đầu tiên, nó xem xem chỉ lệnh được yêu cầu bởi CPU có trong L1 instruction cache hay không. Nếu không có ở đây, nó sẽ vào L2 memory cache. Nếu chỉ lệnh cũng không có trong L2 memory cache thì nó sẽ phải nạp trực tiếp từ bộ nhớ RAM.

Khi bạn bật máy tính, tất cả các cache đều trống rỗng, tuy nhiên khi hệ thống bắt đầu nạp hệ điều hành, CPU bắt đầu xử lý các chỉ lệnh đầu tiên từ ổ cứng và cache controller bắt đầu nạp các cache và đó là những gì bắt đầu để chuẩn bị thực hiện xử lý một chỉ lệnh.

Sau khi khối tìm nạp đã có được chỉ lệnh cần thiết cho CPU để được xử lý, nó gửi chỉ lệnh này đến khối giải mã.

Khối giải mã sẽ chỉ ra chỉ lệnh này thực hiện những nhiệm vụ gì. Nó thực hiện điều đó bằng cách hỏi ý kiến bộ nhớ ROM tồn tại bên trong CPU, được gọi là microcode. Mỗi chỉ lệnh mà CPU hiểu đều có một microcode của nó. Microcode sẽ “ra lệnh” cho CPU thực hiện những gì. Nó giống như hướng dẫn từng bước trong các tài liệu hướng dẫn. Ví dụ, nếu chỉ lệnh đã nạp bổ sung a+b thì microcode của nó sẽ bảo với khối giải mã rằng nó cần có hai tham số a và b. Khối giải mã sau đó sẽ yêu cầu khối tìm nạp lấy dữ liệu có trong hai vị trí nhớ kế tiếp, phù hợp với các giá trị của a và b. Sau khi khối giải mã “dịch” xong chỉ lệnh và lấy được tất cả dữ liệu cần thiết để thực thi chỉ lệnh, nó sẽ gửi tất cả dữ liệu này và hướng dẫn từng bước về cách thực thi chỉ lệnh đó đến khối thực thi.

Khối thực thi sẽ thực thi chỉ lệnh này. Trên các CPU hiện đại, bạn sẽ thấy có nhiều khối thực thi làm việc song song. Điều này được thực hiện để tăng hiệu suất của CPU. Ví dụ, một CPU có 6 khối thực thi sẽ có thể thực thi đến 6 chỉ lệnh song song đồng thời, chính vì vậy theo lý thuyết nó hoàn toàn có thể thực hiện được một hiệu suất bằng với 6 bộ vi xử lý mà chỉ có một khối thực thi. Kiểu kiến trúc này được gọi là kiến trúc “superscalar”.

Thông thường các CPU hiện đại không có nhiều khối thực thi giống nhau; chúng có các khối thực thi dành riêng cho mỗi loại chỉ lệnh. Một ví dụ dễ hiểu nhất ở đây là FPU, Float Point Unit, khối chịu trách nhiệm thực thi các chỉ lệnh toán học phức tạp. Thường giữa khối giải mã và khối thực thi có một khối (gọi là khối gửi đi hoặc lập biểu) chịu trách nhiệm về việc gửi chỉ lệnh đến đúng khối thực thi, có nghĩa là nếu là một chỉ lệnh toán học thì nó sẽ gửi chỉ lệnh đó đến FPU chứ không gửi đến khối thực thi chung. Cũng vì vậy các khối thực thi chung được gọi là ALU (Arithmetic and Logic Unit).

Cuối cùng, khi việc xử lý được thực hiện, các kết quả sẽ được gửi đến L1 data cache. Tiếp tục ví dụ a+b của chúng ta, kết quả sẽ được gửi ra L1 data cache. Kết quả này có thế sau đó được gửi lại đến bộ nhớ RAM hoặc đến một địa điểm khác như video card chẳng hạn. Tuy nhiên điều này sẽ phụ thuộc vào chỉ lệnh kế tiếp sẽ được xử lý tiếp theo (chỉ lệnh kế tiếp có thể là in kết quả ra màn hình).

Một tính năng thú vị khác mà tất cả các bộ vi xử lý đều có đó là “pipeline” – trong thiết kế máy tính đây là một tuyến lắp ráp thuộc phần cứng làm tăng tốc độ xử lý các lệnh thông qua quá trình thực hiện, truy tìm và ghi trở lại. Thiết kế này có khả năng có một số chỉ lệnh khác ở một số tầng khác của CPU ở cùng thời điểm.

Sau khi khối tìm nạp đã gửi chỉ lệnh đến khối giải mã, nó sẽ không làm gì (nhàn rỗi)? Vậy về việc thay thế không làm gì bằng cách cho khối này lấy chỉ lệnh kế tiếp thì sao? Khi chỉ lệnh đầu tiên vào tới khối thực thi, khối chỉ lệnh có thể gửi chỉ lệnh thứ hai đến khối giải mã và lấy chỉ lệnh thứ ba, và quá trình cứ tiếp tục như vậy.

Trong CPU hiện đại có pipeline 11 tầng (mỗi tầng là một khối của CPU), nó sẽ có thể có đến 11 chỉ lệnh bên trong tại cùng một thời điểm. Trong thực tế, khi tất cả các CPU hiệu đại đều có kiến trúc “superscalar“ thì số chỉ lệnh đồng thời bên trong CPU sẽ cao hơn.

Cũng vậy, với CPU pipeline có 11 tầng, một chỉ lệnh được thực thi hoàn toàn sẽ phải chuyển qua 11 khối. Nếu càng có nhiều số tầng hay khối như vậy thì lượng thời gian mà mỗi chỉ lệnh giữ chậm để được thực thi sẽ nhiều hơn. Hay nói cách khác, hãy nhớ rằng một số chỉ lệnh có thể chạy bên trong CPU cùng một thời điểm. Chỉ lệnh đầu tiên đã nạp bởi CPU có thể giữ chậm 11 bước để được xử lý xong, nhưng khi nó đi ra thì chỉ lệnh thứ hai sẽ cũng được xử lý ngay sau đó (chỉ mất một số bước giữ chậm chứ không phải là toàn bộ 11 tầng).

Có một số mẹo khác được sử dụng bởi các CPU hiện đại nhằm tăng hiệu suất hệ thống. Chúng tôi sẽ giới thiệu hai trong số chúng, đó là thực thi không tuân theo thứ tự (OOO) và thực thi có suy đoán

Thực thi không tuân theo thứ tự (OOO)

Hãy nhớ rằng chúng tôi đã nói rằng các CPU hiện đại có một số khối thực thi làm việc song song và có một số kiểu khác đối với các khối thực thi, như ALU - khối thực thi chung, và FPU – khối thực thi toán học. Hãy lấy một ví dụ chung để hiểu rõ vấn đề này, chúng ta hãy cho CPU ví dụ có 6 cỗ máy thực thi, 4 chỉ lệnh chung (generic instruction) cho ALU và 2 chỉ lệnh toán học (math instruction) cho FPU. Chúng ta cũng cho rằng chương trình có thứ tự chỉ lệnh dưới đây.

1. chỉ lệnh chung (ALU)
2. chỉ lệnh chung
3. chỉ lệnh chung
4. chỉ lệnh chung
5. chỉ lệnh chung
6. chỉ lệnh chung
7. chỉ lệnh toán học (FPU)
8. chỉ lệnh chung
9. chỉ lệnh chung
10. chỉ lệnh toán học

Điều gì sẽ xảy ra? Khối gửi đi/lập lịch sẽ gửi 4 chỉ lệnh đầu tiên đến các khối ALU nhưng sau đó chỉ lệnh thứ 5 CPU sẽ cần phải đợi cho một chỉ lệnh của ALU của chúng được giải phóng để tiếp tục xử lý, vì lúc này tất cả 4 khối thực thi chung đều bận cả. Điều này không tốt bởi vì chúng ta vẫn có 2 chỉ khối toán học (FPU) chưa dùng đến, rõ ràng chúng đang trong chế độ nhàn rỗi. Chính vì vậy, một thực thi không tuân theo thứ tự (OOO) (tất cả các CPU hiện đại đều có tính năng này) sẽ xem chỉ lệnh kế tiếp xem nó có thể được gửi đến một trong hai khối thực thi đang nhàn rỗi kia không. Trong ví dụ của chúng ta, nó không thể, vì chỉ lệnh thứ 6 cũng cần đến một khối thực thi chung (ALU) để xử lý. Cỗ máy thực thi không tuân theo thứ tự vẫn tiếp tục công việc tìm kiếm của nó và tìm ra rằng chỉ lệnh thứ 7 là một chỉ lệnh toán học và có thể được thực thi tại khối thực thi toán học đang nhàn rỗi. Do các khối thực thi toán học khác vẫn đang nhàn rỗi nên nó sẽ vào chương trình để tìm kiếm chỉ lệnh toán học khác. Trong ví dụ của chúng ta, nó sẽ nhảy qua chỉ lệnh thứ 8 và 9 và nạp chỉ lệnh thứ 10.

Trong ví dụ của chúng ta, các khối thực thi sẽ luôn xử lý tại cùng một thời điểm, các chỉ lệnh được thực thi lúc này là chỉ lệnh thứ 1, 2, 3, 4, 7 và 10.

Tên OOO đến từ thực tế rằng CPU không cần phải đợi mà nó có thể kéo một chỉ lệnh ở cuối chương trình và xử lý nó trước các chỉ lệnh ở trên. Rõ ràng cỗ máy thực thi không tuân theo thứ tự OOO không thể mãi tìm kiếm một chỉ lệnh nếu không có chỉ lệnh nào cần (ví dụ như trong ví dụ trên là không có chỉ lệnh toán học chẳng hạn). Cỗ máy này của tất cả các CPU có một giới hạn nhất định về số lượng chỉ lệnh mà có có thể tìm (thường là 512).

Thực thi có suy đoán

Hãy cho rằng một trong những chỉ lệnh chung là một chỉ lệnh rẽ nhánh có điều kiện. Vậy cỗ máy thực thi OOO sẽ thực hiện những gì? Nếu CPU bổ sung một tính năng gọi là thực thi có suy đoán (tất cả các CPU hiện đại đều có), nó sẽ thực thi cả hai nhánh. Xem xét ví dụ bên dưới.

1. chỉ lệnh chung
2. chỉ lệnh chung
3. nếu a=<b tới chỉ lệnh 15
4. chỉ lệnh chung
5. chỉ lệnh chung
6. chỉ lệnh chung
7. chỉ lệnh toán học
8. chỉ lệnh chung
9. chỉ lệnh chung
10. chỉ lệnh toán học
...
15. chỉ lệnh toán học
16. chỉ lệnh chung 
...

Khi cỗ máy thực thi không theo thứ tự phân tích chương trình này, nó sẽ kéo chỉ lệnh 15 vào FPU, lúc này FPU đang nhàn rỗi. Chính vì vậy tại thời điểm này, chúng ta có cả hai nhánh cùng được xử lý đồng thời. Nếu khi CPU kết thúc việc xử lý chỉ lệnh thứ ba biết được a>b thì CPU sẽ loại bỏ việc xử lý của chỉ lệnh 15. Bạn có thể nghĩ điều này gây tốn thời gian nhưng trong thực tế nó hoàn toàn không tốn thời gian. Nó hoàn toàn không đáng bao nhiêu để CPU thực thi chỉ lệnh riêng đó, vì FPU kiểu gì cũng nhàn rỗi. Mặt khác nếu a=<b thì CPU sẽ có được mức lợi về hiệu suất ở đây, vì khi chỉ lệnh thứ ba yêu cầu chỉ lệnh 15, đây là chỉ lệnh đã được xử lý rồi, tiếp theo đó là chỉ lệnh 16, và các chỉ lệnh sau đó. Chỉ lệnh 16 cũng đã được xử lý bởi cỗ máy thực thi không theo thứ tự.