2010-06-16 18 views
17

Có điều gì đó làm tôi nhớ đến mô hình bộ nhớ Java (nếu tôi thậm chí hiểu mọi thứ một cách chính xác). Nếu có hai luồng A và B, không có gì đảm bảo rằng B sẽ thấy giá trị được viết bởi A, trừ khi cả A và B đồng bộ trên cùng một màn hình.Mô hình bộ nhớ Java (JSR-133) có ngụ ý rằng việc nhập một màn hình sẽ xóa bộ nhớ cache dữ liệu CPU không?

Đối với bất kỳ kiến ​​trúc hệ thống nào đảm bảo sự kết hợp bộ đệm giữa các luồng, không có vấn đề gì. Nhưng nếu kiến ​​trúc không hỗ trợ kết nối bộ nhớ cache trong phần cứng, điều này về cơ bản có nghĩa là bất cứ khi nào một luồng vào màn hình, tất cả các thay đổi bộ nhớ được thực hiện trước đó phải được cam kết với bộ nhớ chính và bộ nhớ cache phải bị vô hiệu. Và nó cần phải là toàn bộ bộ nhớ cache dữ liệu, không chỉ là một vài dòng, vì màn hình không có thông tin mà các biến trong bộ nhớ mà nó bảo vệ. Nhưng điều đó chắc chắn sẽ tác động đến hiệu suất của bất kỳ ứng dụng nào cần phải đồng bộ hóa thường xuyên (đặc biệt là những thứ như hàng đợi công việc với các công việc ngắn). Vì vậy, Java có thể làm việc hợp lý tốt trên kiến ​​trúc mà không có phần cứng cache-coherency? Nếu không, tại sao mô hình bộ nhớ không đảm bảo mạnh hơn về khả năng hiển thị? Nó sẽ không hiệu quả hơn nếu ngôn ngữ yêu cầu thông tin được bảo vệ bởi một màn hình?

Như tôi thấy, mô hình bộ nhớ cho chúng ta điều tồi tệ nhất của cả hai thế giới, nhu cầu tuyệt đối để đồng bộ hóa, ngay cả khi sự kết hợp bộ nhớ cache được đảm bảo trong phần cứng và mặt khác hiệu suất kém trên kiến ​​trúc không mạch lạc (bộ nhớ cache đầy đủ). Vì vậy, không nên nó nghiêm ngặt hơn (yêu cầu thông tin được bảo vệ bởi một màn hình) hoặc mất nhiều hơn và hạn chế nền tảng tiềm năng để kiến ​​trúc cache-mạch lạc?

Hiện tại, nó không có ý nghĩa gì đối với tôi. Ai đó có thể giải thích lý do tại sao mô hình bộ nhớ cụ thể này được chọn không?


EDIT: Việc sử dụng nghiêm ngặt và mất của tôi là một lựa chọn không tốt khi nhìn lại. Tôi đã sử dụng "nghiêm ngặt" đối với trường hợp có ít bảo lãnh hơn và "thua" đối diện. Để tránh nhầm lẫn, nó có thể tốt hơn để nói về đảm bảo mạnh hơn hoặc yếu hơn.

+0

Một điều cần được nhận xét là câu lệnh "đồng bộ hóa trên cùng một màn hình". Khi tôi hiểu nó đồng bộ hóa trên cùng một màn hình đảm bảo trật tự thực hiện xung quanh màn hình đó. Tuy nhiên, về mặt bộ nhớ cache, việc thu thập khối _any_ đồng bộ buộc tải lại bộ đệm và giải phóng _any_ lock làm cho tất cả các ghi được ghi lại từ bộ nhớ CPU hoạt động. – Gray

+0

Bạn đã lưu ý đến kiến ​​trúc cụ thể nào? – curiousguy

Trả lời

6

cần tuyệt đối để đồng bộ hóa, thậm chí nếu sự mạch lạc bộ nhớ cache được đảm bảo trong phần cứng

Vâng, nhưng sau đó bạn chỉ cần lý do chống lại Memory Model Java, không chống lại một kiến ​​trúc phần cứng cụ thể mà chương trình của bạn sẽ diễn ra. Ngoài ra, nó không chỉ về phần cứng, trình biên dịch và JIT có thể sắp xếp lại các hướng dẫn gây ra vấn đề về khả năng hiển thị. Các cấu trúc đồng bộ hóa trong khả năng hiển thị địa chỉ Java & nguyên tử luôn ở tất cả các mức chuyển đổi mã có thể (ví dụ: trình biên dịch/JIT/CPU/bộ nhớ cache).

và về việc thực hiện xấu Mặt khác trên kiến ​​trúc rời rạc (full bừng bộ nhớ cache)

Có lẽ tôi hiểu lầm s/t, nhưng với kiến ​​trúc mạch lạc, bạn phải đồng bộ hóa phần quan trọng anyway. Nếu không, bạn sẽ chạy vào tất cả các loại điều kiện chủng tộc do sắp xếp lại. Tôi không hiểu tại sao Mô hình bộ nhớ Java lại làm cho vấn đề trở nên tồi tệ hơn.

nó không nên khắt khe hơn (yêu cầu thông tin gì được bảo vệ bởi một màn hình )

Tôi không nghĩ rằng nó có thể nói với CPU để tuôn ra bất kỳ phần riêng biệt của bộ nhớ cache ở tất cả. Trình biên dịch tốt nhất có thể làm là phát ra hàng rào bộ nhớ và để cho CPU quyết định phần nào của bộ nhớ cache cần xả - nó vẫn thô ráp hơn những gì bạn đang tìm kiếm. Thậm chí nếu có thể kiểm soát tốt hơn, tôi nghĩ rằng nó sẽ làm cho lập trình đồng thời thậm chí còn khó khăn hơn (đã đủ khó khăn).

AFAIK, Java 5 MM (giống như .NET CLR MM) là "nghiêm ngặt" hơn các mô hình bộ nhớ của các kiến ​​trúc phổ biến như x86 và IA64. Vì vậy, nó làm cho lý do về nó tương đối đơn giản hơn. Tuy nhiên, nó rõ ràng không nên cung cấp s/t gần gũi hơn với tính nhất quán tuần tự bởi vì điều đó sẽ làm tổn thương hiệu suất đáng kể khi ít trình biên dịch/tối ưu hóa JIT/CPU/cache có thể được áp dụng.

+0

Tôi havent đặt bất kỳ suy nghĩ về sắp xếp lại các vấn đề, nhưng nếu một ranh giới sắp xếp lại là cần thiết, chỉ cần truy cập vào một lĩnh vực dễ bay hơi sẽ cung cấp một mà không cần phải đồng bộ hóa. Nếu mô hình bộ nhớ bao gồm sự bảo đảm về khả năng hiển thị của các trường không bay hơi, một trường dễ bay hơi có thể được sử dụng để truyền đạt hiệu lực của bất kỳ số trường không bay hơi nào giữa hai luồng (mặc dù với chi phí bỏ phiếu là một trường biến động). – Durandal

+0

Về kiến ​​trúc không mạch lạc, đối với một trường dễ bay hơi, JIT có thể sử dụng các hướng dẫn bỏ qua bộ nhớ cache HOẶC nếu những người đó vắng mặt chỉ cần tuôn ra dòng bộ nhớ cache duy nhất bao gồm trường. Với ngữ nghĩa của đồng bộ, không có giới hạn (các) trường nào được xuất bản sang một luồng khác, do đó tôi kết luận rằng toàn bộ bộ nhớ đệm cần được xóa trong trường hợp đó. Đó là lỗi của tôi. Flushing toàn bộ bộ nhớ cache (có khả năng megabyte bộ nhớ cache bẩn), ngay cả khi chỉ cần một vài lĩnh vực cần phải được công bố cho một chủ đề có vẻ không hiệu quả (tất nhiên nó phụ thuộc rất cao vào các ứng dụng) – Durandal

+0

Tôi phải thừa nhận rằng tôi chỉ có kiến ​​thức thân mật của một gia đình bộ vi xử lý và một trong đó bao gồm các hướng dẫn để tuôn ra/làm mất hiệu lực các dòng đơn, cũng như một trang bộ nhớ (như trong trang MMU).Vì đây là một khía cạnh hiệu suất có liên quan cho các trình điều khiển thiết bị thực hiện DMA, tôi cho rằng bất kỳ kiến ​​trúc hợp lý nào đều có cách để thực hiện điều này. Nhưng tôi có thể hoàn toàn sai với giả định này. – Durandal

1

bộ nhớ cache mà JVM có quyền truy cập thực sự chỉ là sổ đăng ký CPU. vì không có nhiều người trong số họ, xả chúng khi màn hình thoát không phải là vấn đề lớn.

EDIT: (nói chung) là bộ nhớ cache không thuộc thẩm quyền của JVM, JVM có thể không chọn để đọc/ghi/tuôn ra những lưu trữ, do đó quên về họ trong cuộc thảo luận này

tưởng tượng mỗi CPU có 1.000.000 đăng ký. JVM vui vẻ khai thác chúng để thực hiện các tính toán nhanh chóng điên rồ - cho đến khi nó va vào màn hình nhập/thoát, và phải xóa 1.000.000 đăng ký cho lớp bộ nhớ cache tiếp theo.

nếu chúng ta sống trong thế giới đó, Java phải đủ thông minh để phân tích những đối tượng không được chia sẻ (phần lớn các đối tượng không), hoặc phải yêu cầu người lập trình làm điều đó.

Mô hình bộ nhớ java là một mô hình lập trình đơn giản cho phép các lập trình viên trung bình tạo các thuật toán đa luồng OK. bởi 'đơn giản hóa' tôi có nghĩa là có thể có 12 người trên toàn thế giới thực sự đã đọc chương 17 của JLS và thực sự hiểu nó.

+2

Tôi không nghĩ rằng điều này là chính xác. CPU cũng có bộ nhớ cache theo thứ tự megabyte trước bộ nhớ chính http://en.wikipedia.org/wiki/CPU_cache. Vấn đề là khi bộ nhớ cache này xóa các trang bẩn của nó sang bộ nhớ chính và vô hiệu hóa các trang đã được cập nhật trong bộ nhớ chính. – Gray

+0

Ngữ nghĩa của bộ đệm và đăng ký là khác nhau - thanh ghi là luồng cục bộ, trong khi bộ đệm có thể được chia sẻ giữa các luồng (hoặc thậm chí là các quy trình, tùy thuộc vào kiến ​​trúc). – Durandal

+0

dudes, bộ nhớ cache bạn nói về không được lập trình bởi JVM, vì vậy chúng không liên quan đến cuộc thảo luận của chúng tôi. – irreputable

4

Câu trả lời là hầu hết các bộ xử lý đa số là bộ nhớ cache kết hợp, bao gồm cả hệ thống NUMA lớn, gần như vậy? luôn luôn là ccNUMA.

Tôi nghĩ bạn hơi bối rối về cách tính nhất quán của bộ nhớ cache được thực hiện trong thực tế. Thứ nhất, bộ nhớ đệm có thể mạch lạc/rời rạc đối với một vài điều khác trên hệ thống với:

  • Devices
  • (Memory sửa đổi theo) DMA
  • cache dữ liệu vs cache hướng dẫn
  • Caches trên lõi khác/bộ vi xử lý (câu hỏi này là về)
  • ...

Điều gì đó phải được thực hiện để duy trì tính nhất quán. Khi làm việc với các thiết bị và DMA, trên các kiến ​​trúc có bộ đệm không liên quan đến DMA/thiết bị, bạn có thể bỏ qua bộ đệm (và có thể là bộ đệm ghi), hoặc làm mất hiệu lực bộ nhớ đệm xung quanh các hoạt động liên quan đến DMA/thiết bị.

Tương tự, khi tạo mã động, bạn có thể cần xóa bộ đệm hướng dẫn.

Khi nói đến bộ đệm CPU, sự kết hợp đạt được bằng cách sử dụng một số giao thức nhất quán, chẳng hạn như MESI, MOESI, ... Các giao thức này xác định các thông điệp được gửi giữa các bộ đệm để đáp ứng với các sự kiện nhất định (ví dụ: yêu cầu vô hiệu lưu trữ khi một đường dẫn không độc quyền được sửa đổi, ...).

Trong khi điều này là đủ để duy trì (cuối cùng) mạch lạc, nó không đảm bảo đặt hàng, hoặc những thay đổi đó ngay lập tức hiển thị cho các CPU khác. Sau đó, cũng có viết bộ đệm, mà trì hoãn viết. Vì vậy, mỗi kiến ​​trúc CPU cung cấp bảo đảm đặt hàng (ví dụ: truy cập trước khi một cửa hàng liên kết không thể sắp xếp lại sau khi lưu trữ) và/hoặc cung cấp hướng dẫn (rào cản bộ nhớ/hàng rào) để yêu cầu bảo đảm như vậy. Cuối cùng, việc nhập/thoát một màn hình không đòi hỏi phải xóa bộ nhớ đệm, nhưng có thể đòi hỏi phải thoát khỏi bộ đệm ghi và/hoặc gian hàng chờ kết thúc đọc.

+0

Tôi biết rằng với sự thống trị của x86 vấn đề có tính chất lý thuyết hơn, nhưng nếu giả định có sự hỗ trợ mạch lạc trong phần cứng thì mô hình bộ nhớ Java có vẻ quá bảo thủ trong định nghĩa của nó. Ví dụ với thiết bị DMA không thực sự làm sáng tỏ chủ đề, vì trình điều khiển cho bất kỳ thiết bị nào như vậy sẽ chịu trách nhiệm xóa bộ đệm khi cần - và không giống như trình điều khiển Java * biết chính xác * vùng bộ nhớ nào sẽ bị ảnh hưởng. – Durandal

+0

Điểm về thiết bị và DMA là để giải thích rằng có nhiều ý nghĩa cho "không mạch lạc", và điều đó có thể gây hiểu lầm cho mọi người tin rằng một bộ nhớ phù hợp với kiến ​​trúc phù thủy là không mạch lạc. – ninjalj

+0

Và về sự kết hợp bộ nhớ cache, như tôi giải thích, cache không liền mạch ngay lập tức, nhưng một giao thức đảm bảo tính nhất quán cuối cùng. Hàng rào ngăn chặn các vấn đề đặt hàng và hiển thị do ghi bộ đệm, hàng đợi không hợp lệ, bộ nhớ cache ngân hàng, ... – ninjalj

5

Kiến trúc hiện có đảm bảo tính nhất quán của bộ nhớ cache, nhưng chúng không đảm bảo tính nhất quán tuần tự - hai điều khác nhau. Kể từ khi seq. tính nhất quán không được đảm bảo, một số sắp xếp lại được cho phép bởi phần cứng và bạn cần các phần quan trọng để hạn chế chúng. Các phần quan trọng đảm bảo rằng những gì một luồng ghi được hiển thị cho một chuỗi khác (tức là chúng ngăn chặn chủng tộc dữ liệu) và chúng cũng ngăn chặn điều kiện chủng tộc cổ điển (nếu hai chủ đề tăng cùng một biến, bạn cần điều đó cho mỗi chuỗi đọc giá trị hiện tại và ghi giá trị mới là không thể tách rời).

Hơn nữa, mô hình thực hiện không đắt như bạn mô tả. Trên hầu hết các kiến ​​trúc hiện có, bộ nhớ cache liên kết nhưng không tuần tự nhất quán, khi bạn nhả khóa, bạn phải tuôn ra việc ghi vào bộ nhớ, và khi bạn có được một cái bạn có thể cần phải làm gì đó để đảm bảo đọc trong tương lai sẽ không đọc các giá trị cũ chủ yếu có nghĩa là chỉ ngăn chặn việc đọc được di chuyển quá sớm, vì bộ nhớ cache được giữ chặt chẽ; nhưng lần đọc vẫn không được di chuyển. Cuối cùng, bạn có vẻ nghĩ rằng Mô hình bộ nhớ của Java (JMM) là đặc biệt, trong khi các nền tảng hiện nay khá hiện đại, và tương tự như khóa Ada, POSIX (tùy thuộc vào việc giải thích tiêu chuẩn). và mô hình bộ nhớ C/C++. Bạn có thể muốn đọc sách dạy nấu JSR-133 giải thích cách JMM được triển khai trên các kiến ​​trúc hiện có: http://g.oswego.edu/dl/jmm/cookbook.html.