tóm tắt: Việc tìm kiếm và khai thác tính song song (mức hướng dẫn) trong một chương trình đơn luồng được thực hiện hoàn toàn bằng phần cứng, bởi lõi CPU mà nó đang chạy. Và chỉ qua một cửa sổ của vài trăm hướng dẫn, không sắp xếp lại quy mô lớn.
Các chương trình đơn luồng không nhận được lợi ích từ CPU đa lõi, ngoại trừ việc các thứ khác có thể chạy trên các lõi khác thay vì mất thời gian khỏi tác vụ đơn luồng.
HĐH tổ chức các hướng dẫn của tất cả các luồng theo cách mà chúng không chờ đợi nhau.
HĐH KHÔNG nhìn vào các luồng lệnh của các luồng. Nó chỉ lập lịch trình chủ đề đến lõi.
Trên thực tế, mỗi lõi chạy chức năng lập lịch của HĐH khi cần tìm ra việc cần làm tiếp theo. Lập lịch là một thuật toán phân tán. Để hiểu rõ hơn về các máy đa lõi, hãy nghĩ mỗi lõi là chạy kernel riêng. Giống như một chương trình đa luồng, kernel được viết để mã của nó trên một lõi có thể tương tác an toàn với mã của nó trên các lõi khác để cập nhật cấu trúc dữ liệu được chia sẻ (như danh sách các luồng đã sẵn sàng để chạy.
Dù sao, HĐH có liên quan đến việc giúp các quy trình đa luồng khai thác song song mức luồng phải được phơi bày rõ ràng bằng cách viết thủ công một chương trình đa luồng . (Hoặc bởi một trình biên dịch song song tự động với OpenMP hoặc một cái gì đó).
Sau đó, mặt trước của CPU tổ chức thêm các hướng dẫn đó bằng cách phân phối một luồng cho mỗi lõi và phân phối các lệnh độc lập từ mỗi luồng trong bất kỳ chu kỳ mở nào.
Lõi CPU chỉ chạy một luồng hướng dẫn, nếu nó không bị dừng (ngủ cho đến khi ngắt tiếp theo, ví dụ như ngắt hẹn giờ). Thường thì đó là một luồng, nhưng nó cũng có thể là một trình xử lý ngắt nhân hoặc mã hạt nhân linh tinh nếu kernel quyết định làm một việc gì đó ngoài việc quay lại luồng trước đó sau khi xử lý và ngắt hoặc gọi hệ thống.
Với HyperThreading hoặc các thiết kế khác của SMT, lõi CPU vật lý hoạt động giống như nhiều lõi "logic". Sự khác biệt duy nhất từ góc độ HĐH giữa CPU bốn nhân với siêu phân luồng (4c8t) và máy 8 lõi đơn giản (8c8t) là HĐH nhận biết HT sẽ cố gắng lên lịch các luồng để tách các lõi vật lý để chúng không ' t cạnh tranh với nhau. Một hệ điều hành không biết về siêu phân luồng sẽ chỉ nhìn thấy 8 lõi (trừ khi bạn tắt HT trong BIOS, thì nó sẽ chỉ phát hiện 4).
Thuật ngữ " front-end" dùng để chỉ một phần của lõi CPU lấy mã máy, giải mã các hướng dẫn và đưa chúng vào phần không theo thứ tự của lõi . Mỗi lõi có mặt trước riêng và nó là một phần của toàn bộ lõi. Hướng dẫn nó tìm nạp là những gì CPU hiện đang chạy.
Bên trong phần không theo thứ tự của lõi, các lệnh (hoặc uops) được gửi đến các cổng thực thi khi các toán hạng đầu vào của chúng đã sẵn sàng và có một cổng thực thi miễn phí. Điều này không phải xảy ra theo thứ tự chương trình, vì vậy đây là cách CPU OOo có thể khai thác song song mức hướng dẫn trong một luồng .
Nếu bạn thay thế "lõi" bằng "đơn vị thực thi" trong ý tưởng của mình, bạn đã gần sửa. Có, CPU không phân phối các lệnh / uops độc lập cho các đơn vị thực thi song song. (Nhưng có một thuật ngữ trộn lẫn, vì bạn đã nói "front-end" khi thực sự đó là bộ lập lịch hướng dẫn của CPU hay còn gọi là Trạm đặt chỗ chọn các lệnh sẵn sàng để thực thi).
Việc thực hiện ngoài đơn hàng chỉ có thể tìm thấy ILP ở cấp độ rất cục bộ, chỉ tối đa vài trăm hướng dẫn, không phải giữa hai vòng độc lập (trừ khi chúng ngắn).
Ví dụ, asm tương đương với điều này
int i=0,j=0;
do {
i++;
j++;
} while(42);
sẽ chạy nhanh như vòng lặp tương tự chỉ tăng một bộ đếm trên Intel Haswell. i++
chỉ phụ thuộc vào giá trị trước đó i
, trong khi j++
chỉ phụ thuộc vào giá trị trước đó j
, vì vậy hai chuỗi phụ thuộc có thể chạy song song mà không phá vỡ ảo tưởng về mọi thứ được thực hiện theo thứ tự chương trình.
Trên x86, vòng lặp sẽ trông giống như thế này:
top_of_loop:
inc eax
inc edx
jmp .loop
Haswell có 4 cổng thực thi số nguyên và tất cả chúng đều có các đơn vị cộng, vì vậy nó có thể duy trì thông lượng lên tới 4 inc
lệnh trên mỗi đồng hồ nếu tất cả đều độc lập. (Với độ trễ = 1, vì vậy bạn chỉ cần 4 thanh ghi để tối đa các thông bằng cách giữ 4 inc
hướng dẫn trong chuyến bay Ngược lại điều này với vector-FP MUL hoặc FMA:. Độ trễ = 5 thông = 0,5 nhu cầu 10 ắc vector để giữ 10 FMAs trong chuyến bay để tối đa hóa thông lượng. Và mỗi vectơ có thể là 256b, giữ 8 phao chính xác đơn).
Nhánh lấy cũng là một nút cổ chai: một vòng lặp luôn mất ít nhất một toàn bộ đồng hồ trên mỗi lần lặp, bởi vì thông lượng của nhánh lấy được giới hạn là 1 trên mỗi đồng hồ. Tôi có thể đặt thêm một lệnh bên trong vòng lặp mà không làm giảm hiệu suất, trừ khi nó cũng đọc / ghi eax
hoặc edx
trong trường hợp đó nó sẽ kéo dài chuỗi phụ thuộc đó. Đặt thêm 2 lệnh trong vòng lặp (hoặc một lệnh đa uop phức tạp) sẽ tạo ra một nút cổ chai ở mặt trước, vì nó chỉ có thể phát 4 vòng trên mỗi đồng hồ vào lõi không theo thứ tự. (Xem phần Hỏi & Đáp này để biết một số chi tiết về những gì xảy ra đối với các vòng lặp không có nhiều 4 vòng: bộ đệm vòng lặp và bộ đệm uop làm cho mọi thứ trở nên thú vị.)
Trong các trường hợp phức tạp hơn, việc tìm kiếm sự song song đòi hỏi phải nhìn vào một cửa sổ hướng dẫn lớn hơn . (ví dụ: có thể có một chuỗi gồm 10 hướng dẫn mà tất cả phụ thuộc vào nhau, sau đó là một số hướng dẫn độc lập).
Dung lượng bộ đệm Re-Order là một trong những yếu tố giới hạn kích thước cửa sổ không theo thứ tự. Trên Intel Haswell, nó là 192 uops. (Và thậm chí bạn có thể đo nó bằng thực nghiệm , cùng với khả năng đổi tên đăng ký (kích thước tệp đăng ký).) Các lõi CPU công suất thấp như ARM có kích thước ROB nhỏ hơn nhiều, nếu chúng thực hiện không theo thứ tự.
Cũng lưu ý rằng CPU cần phải được sắp xếp theo đường ống, cũng như không theo thứ tự. Vì vậy, nó phải tìm nạp và giải mã các hướng dẫn trước các lệnh được thực thi, tốt nhất là có đủ thông lượng để nạp lại bộ đệm sau khi bỏ lỡ bất kỳ chu kỳ tìm nạp nào. Các chi nhánh rất phức tạp, bởi vì chúng ta không biết lấy từ đâu nếu chúng ta không biết một nhánh đi theo hướng nào. Đây là lý do tại sao dự đoán chi nhánh rất quan trọng. (Và tại sao các CPU hiện đại sử dụng thực thi đầu cơ: họ đoán rằng một nhánh sẽ đi theo hướng nào và bắt đầu tìm nạp / giải mã / thực thi luồng lệnh đó. Khi phát hiện sai, chúng quay trở lại trạng thái tốt nhất đã biết và thực hiện từ đó.)
Nếu bạn muốn đọc thêm về phần bên trong CPU, có một số liên kết trong wiki thẻ Stackoverflow x86 , bao gồm hướng dẫn microarch của Agner Fog , và các bản ghi chi tiết của David Kanter với sơ đồ về CPU Intel và AMD. Từ bản ghi vi kiến trúc Intel Haswell của mình , đây là sơ đồ cuối cùng của toàn bộ đường ống của lõi Haswell (không phải toàn bộ chip).
Đây là một sơ đồ khối của một đơn lõi CPU . Một CPU lõi tứ có 4 trong số này trên một chip, mỗi chip có bộ nhớ L1 / L2 riêng (chia sẻ bộ đệm L3, bộ điều khiển bộ nhớ và kết nối PCIe với các thiết bị hệ thống).
Tôi biết điều này là vô cùng phức tạp. Bài viết của Kanter cũng cho thấy các phần của điều này để nói về frontend riêng biệt với các đơn vị thực thi hoặc bộ đệm, chẳng hạn.