16

Quan hệ nhân quả trong JMM có vẻ là phần khó hiểu nhất của nó. Tôi có một số câu hỏi liên quan đến quan hệ nhân quả JMM và hành vi được phép trong các chương trình đồng thời.Tại sao hành vi này được phép trong Mô hình bộ nhớ Java?

Như tôi đã biết, JMM hiện tại luôn cấm các vòng nhân quả. (Tôi có phải không?)

Bây giờ, theo các tài liệu JSR-133, trang 24, Fig.16, chúng ta có một ví dụ trong đó:

Ban đầu x = y = 0

Chủ đề 1:

r3 = x; 
if (r3 == 0) 
    x = 42; 
r1 = x; 
y = r1; 

Chủ đề 2:

r2 = y; 
x = r2; 

Trực quan, r1 = r2 = r3 = 42 dường như không thể. Tuy nhiên, nó không chỉ được đề cập ở mức có thể, mà còn được 'cho phép' trong JMM.

Đối với khả năng, giải thích từ tài liệu mà tôi không hiểu là:

Một trình biên dịch có thể xác định rằng các giá trị duy nhất từng được gán cho x là 0 và 42. Từ đó, trình biên dịch có thể suy luận rằng, tại điểm nơi chúng tôi thực hiện r1 = x, chúng tôi vừa thực hiện ghi 42 đến x hoặc chúng tôi vừa đọc x và thấy giá trị 42. Trong cả hai trường hợp, nó sẽ là hợp pháp để đọc x để xem giá trị 42. Sau đó, nó có thể thay đổi r1 = x thành r1 = 42; điều này sẽ cho phép y = r1 được chuyển thành y = 42 và được thực hiện trước đó, dẫn đến hành vi được đề cập. Trong trường hợp này, ghi số y được cam kết trước tiên.

Câu hỏi của tôi là, loại tối ưu hóa trình biên dịch nào thực sự? (Tôi là trình biên dịch-dốt nát.) Vì 42 được viết chỉ có điều kiện, khi câu lệnh if được thỏa mãn, làm thế nào trình biên dịch có thể quyết định đi bằng văn bản của x? Thứ hai, ngay cả khi trình biên dịch thực hiện tối ưu hóa đầu cơ này, và cam kết y = 42 và thì cuối cùng làm cho r3 = 42, không phải là nó vi phạm vòng lặp nhân quả, vì không còn nguyên nhân và sự khác biệt có hiệu lực bây giờ không? Không.

Trong thực tế, có một ví dụ trong cùng một tài liệu (trang 15, Hình 7) trong đó vòng lặp nhân quả tương tự được đề cập là không thể chấp nhận.

Vậy cách thực hiện lệnh này là hợp pháp trong JMM?

Trả lời

6

Như đã giải thích, các giá trị duy nhất từng ghi vào x là 0 và 42. Chủ đề 1:

r3 = x; // here we read either 0 or 42 
if (r3 == 0) 
    x = 42; 
// at this point x is definitely 42 
r1 = x; 

Do đó trình biên dịch JIT có thể viết lại r1 = x như r1 = 42, và tiếp tục y = 42.Vấn đề là, Chủ đề 1 sẽ luôn, vô điều kiện ghi 42 đến y. Biến số r3 thực tế là thừa và có thể được loại bỏ hoàn toàn khỏi mã máy. Vì vậy, mã trong ví dụ chỉ cho sự xuất hiện của một mũi tên nhân quả từ x đến y, nhưng phân tích chi tiết cho thấy rằng trên thực tế không có quan hệ nhân quả. Kết quả đáng ngạc nhiên là việc viết thư cho y có thể được cam kết sớm.

Lưu ý chung về tối ưu hóa: Tôi lấy nó, bạn đã quen thuộc với các hình phạt về hiệu suất liên quan đến việc đọc từ bộ nhớ chính. Đó là lý do tại sao trình biên dịch JIT bị từ chối không làm điều đó bất cứ khi nào có thể, và trong ví dụ này nó chỉ ra rằng nó không thực sự cần phải đọc x để biết phải viết gì lên y.

Một lưu ý chung về ký hiệu: r1, r2, r3biến địa phương (họ có thể là trên stack hay trong thanh ghi CPU); x, ybiến được chia sẻ (các bộ nhớ này nằm trong bộ nhớ chính). Nếu không tính đến điều này, các ví dụ sẽ không có ý nghĩa.

1

Giá trị của nó không có giá trị là javac không tối ưu hóa mã ở mức độ đáng kể. JIT tối ưu hóa mã nhưng khá thận trọng về mã đặt hàng lại. CPU có thể sắp xếp lại việc thực hiện và nó thực hiện điều này ở mức độ khá nhỏ.

Buộc CPU không thực hiện tối ưu hóa cấp độ hướng dẫn là khá tốn kém, ví dụ: nó có thể làm chậm nó xuống bởi một yếu tố 10 hoặc nhiều hơn. AFAIK, các nhà thiết kế Java muốn xác định tối thiểu các bảo đảm cần thiết sẽ hoạt động hiệu quả trên hầu hết các CPU.

3

trình biên dịch có thể thực hiện một số phân tích và tối ưu hóa và kết thúc bằng sau mã cho thread1:

y=42; // step 1 
r3=x; // step 2 
x=42; // step 3 

Đối với thực đơn luồng, mã này là tương đương với mã gốc và như vậy là hợp pháp. Sau đó, nếu mã của Thread2 được thực thi giữa bước 1 và bước 2 (có thể thực hiện được), thì r3 cũng được gán 42.

Toàn bộ ý tưởng của mẫu mã này là để chứng minh sự cần thiết của đồng bộ hóa thích hợp.

+0

@Alexei Điều đó giải thích một số điều đó. Nhưng, không nên trình biên dịch làm cho nó 'r3 = 0' thay vì 'r3 = 42'? Hoặc họ chỉ cho thấy 'một khả năng'! – gaganbm

+2

Trình biên dịch không tạo ra 'r3 = 42', nó chỉ để lại' r3 = x' nguyên vẹn. Tối ưu hóa trình biên dịch không phải lúc nào cũng được thực hiện ở độ sâu tối đa. Nếu có khả năng tối thiểu là việc tối ưu hóa có thể vi phạm chính xác, nó bị bỏ rơi. Trong mã đã cho, không có trường hợp như vậy, nhưng chúng có thể xuất hiện nếu một số mã khác có mặt. Bên cạnh đó, trình biên dịch có thể quyết định rằng 'r3 = 0' có cùng giá với' r3 = x'. –

Các vấn đề liên quan