Câu hỏi C ++ 11 đã giới thiệu một mô hình bộ nhớ tiêu chuẩn hóa. Nó có nghĩa là gì? Và làm thế nào nó sẽ ảnh hưởng đến lập trình C ++?


C ++ 11 đã giới thiệu một mô hình bộ nhớ tiêu chuẩn hóa, nhưng chính xác điều đó có nghĩa là gì? Và làm thế nào nó sẽ ảnh hưởng đến lập trình C ++?

bài viết này (bởi Gavin Clarke người trích dẫn Herb Sutter) nói rằng,

Mô hình bộ nhớ có nghĩa là mã C ++   hiện có thư viện chuẩn để gọi   bất kể ai đã làm trình biên dịch   và trên nền tảng nào nó đang chạy.   Có một cách tiêu chuẩn để kiểm soát cách thức   chủ đề khác nhau nói chuyện với   bộ nhớ của bộ vi xử lý.

"Khi bạn nói về việc chia nhỏ   [code] trên các lõi khác nhau   trong tiêu chuẩn, chúng ta đang nói về   mô hình bộ nhớ. Chúng ta sắp   tối ưu hóa nó mà không vi phạm   những giả định sau đây mọi người sẽ   để thực hiện trong mã, " Sutter nói.

Tôi có thể ghi nhớ đoạn này và các đoạn tương tự có sẵn trực tuyến (vì tôi đã có mô hình bộ nhớ riêng của mình từ khi sinh: P) và thậm chí có thể đăng câu trả lời cho các câu hỏi mà người khác hỏi, nhưng thành thật mà nói, tôi không hiểu chính xác điều này.

Vì vậy, những gì tôi về cơ bản muốn biết là, lập trình C ++ được sử dụng để phát triển các ứng dụng đa luồng thậm chí trước đây, vì vậy làm thế nào nó quan trọng nếu nó là chủ đề POSIX, hoặc Windows chủ đề, hoặc C + + 11 đề? Những lợi ích là gì? Tôi muốn hiểu chi tiết cấp thấp.

Tôi cũng nhận được cảm giác này rằng mô hình bộ nhớ C ++ 11 là bằng cách nào đó liên quan đến hỗ trợ đa luồng đa phương tiện C ++ 11, vì tôi thường thấy hai cái này cùng nhau. Nếu đúng, chính xác thì sao? Tại sao họ nên liên quan?

Vì tôi không biết làm thế nào nội bộ của đa luồng hoạt động, và những gì mô hình bộ nhớ có nghĩa là nói chung, xin vui lòng giúp tôi hiểu những khái niệm này. :-)


1555
2018-06-11 23:30


gốc




Các câu trả lời:


Đầu tiên, bạn phải học cách suy nghĩ giống như một luật sư ngôn ngữ.

Đặc tả C ++ không tham chiếu đến bất kỳ trình biên dịch, hệ điều hành hoặc CPU cụ thể nào. Nó ám chỉ đến một máy trừu tượng đó là sự tổng quát hóa các hệ thống thực tế. Trong thế giới Luật sư Ngôn ngữ, công việc của lập trình viên là viết mã cho máy trừu tượng; công việc của trình biên dịch là hiện thực hóa mã đó trên một máy cụ thể. Bằng cách mã hóa chặt chẽ với spec, bạn có thể chắc chắn rằng mã của bạn sẽ biên dịch và chạy mà không sửa đổi trên bất kỳ hệ thống nào có trình biên dịch C ++ phù hợp, cho dù hôm nay hoặc 50 năm kể từ bây giờ.

Máy trừu tượng trong đặc tả C ++ 98 / C ++ 03 về cơ bản là một luồng đơn. Vì vậy, không thể viết mã C ++ đa luồng có "hoàn toàn di động" đối với thông số kỹ thuật. Thông số kỹ thuật thậm chí không nói bất cứ điều gì về nguyên tử bộ nhớ tải và cửa hàng hoặc gọi món trong đó tải và các cửa hàng có thể xảy ra, không bao giờ nhớ những thứ như mutexes.

Tất nhiên, bạn có thể viết mã đa luồng trong thực tế cho các hệ thống cụ thể cụ thể - như pthreads hoặc Windows. Nhưng không có Tiêu chuẩncách viết mã đa luồng cho C ++ 98 / C ++ 03.

Máy trừu tượng trong C ++ 11 là đa luồng theo thiết kế. Nó cũng có một xác định rõ mô hình bộ nhớ; có nghĩa là, nó nói những gì trình biên dịch có thể và không thể làm gì khi nói đến việc truy cập bộ nhớ.

Hãy xem xét ví dụ sau, trong đó một cặp biến toàn cầu được truy cập đồng thời bởi hai luồng:

           Global
           int x, y;

Thread 1            Thread 2
x = 17;             cout << y << " ";
y = 37;             cout << x << endl;

Điều gì có thể Thread 2 đầu ra?

Theo C ++ 98 / C ++ 03, đây không phải là hành vi không xác định; bản thân câu hỏi là vô nghĩa bởi vì tiêu chuẩn không chiêm ngưỡng bất cứ thứ gì gọi là "chuỗi".

Theo C ++ 11, kết quả là hành vi không xác định, vì tải và các cửa hàng không cần phải là nguyên tử nói chung. Mà có thể không có vẻ như nhiều cải tiến ... Và bản thân nó không phải.

Nhưng với C ++ 11, bạn có thể viết điều này:

           Global
           atomic<int> x, y;

Thread 1                 Thread 2
x.store(17);             cout << y.load() << " ";
y.store(37);             cout << x.load() << endl;

Bây giờ mọi thứ trở nên thú vị hơn nhiều. Trước hết, hành vi ở đây là xác định. Chủ đề 2 hiện có thể in 0 0 (nếu nó chạy trước Thread 1), 37 17 (nếu nó chạy sau Chủ đề 1), hoặc 0 17 (nếu nó chạy sau khi Thread 1 gán cho x nhưng trước khi nó gán cho y).

Những gì nó không thể in là 37 0, bởi vì chế độ mặc định cho tải / lưu trữ nguyên tử trong C ++ 11 là để thực thi tính nhất quán tuần tự. Điều này chỉ có nghĩa là tất cả các tải và các cửa hàng phải được "như thể" chúng xảy ra theo thứ tự bạn đã viết chúng trong mỗi thread, trong khi hoạt động giữa các chủ đề có thể được xen kẽ nhưng hệ thống thích. Vì vậy, hành vi mặc định của nguyên tử cung cấp cả hai nguyên tử và sắp đặt cho tải và cửa hàng.

Bây giờ, trên một CPU hiện đại, đảm bảo tính nhất quán tuần tự có thể tốn kém. Đặc biệt, trình biên dịch có khả năng phát ra các rào cản bộ nhớ đầy đủ giữa mọi truy cập ở đây. Nhưng nếu thuật toán của bạn có thể chịu được tải và cửa hàng không theo thứ tự; tức là, nếu nó đòi hỏi nguyên tử nhưng không yêu cầu; tức là, nếu nó có thể chịu đựng được 37 0 là đầu ra từ chương trình này, sau đó bạn có thể viết điều này:

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_relaxed);   cout << y.load(memory_order_relaxed) << " ";
y.store(37,memory_order_relaxed);   cout << x.load(memory_order_relaxed) << endl;

CPU càng hiện đại thì càng có khả năng nhanh hơn ví dụ trước.

Cuối cùng, nếu bạn chỉ cần giữ các tải và cửa hàng cụ thể theo thứ tự, bạn có thể viết:

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_release);   cout << y.load(memory_order_acquire) << " ";
y.store(37,memory_order_release);   cout << x.load(memory_order_acquire) << endl;

Điều này đưa chúng ta trở lại với các tải và cửa hàng được đặt hàng - vì vậy 37 0 không còn là đầu ra có thể nữa - nhưng nó làm như vậy với chi phí tối thiểu. (Trong ví dụ tầm thường này, kết quả là giống như toàn vẹn tuần tự toàn diện; trong một chương trình lớn hơn, nó sẽ không được.)

Tất nhiên, nếu các kết quả đầu ra duy nhất bạn muốn xem là 0 0 hoặc là 37 17, bạn có thể chỉ cần bọc một mutex quanh mã gốc. Nhưng nếu bạn đã đọc điều này đến nay, tôi đặt cược bạn đã biết cách hoạt động, và câu trả lời này đã được lâu hơn tôi dự định :-).

Vì vậy, dòng dưới cùng. Mutexes rất tuyệt, và C ++ 11 tiêu chuẩn hóa chúng. Nhưng đôi khi vì lý do hiệu suất bạn muốn nguyên thủy cấp thấp hơn (ví dụ: cổ điển -mẫu khóa được kiểm tra kép). Tiêu chuẩn mới này cung cấp các tiện ích cấp cao như mutexes và biến điều kiện, đồng thời nó cũng cung cấp các tiện ích cấp thấp như các loại nguyên tử và các hương vị khác nhau của rào cản bộ nhớ. Vì vậy, bây giờ bạn có thể viết các hoạt động phức tạp, hiệu suất cao đồng thời hoàn toàn trong ngôn ngữ được chỉ định theo tiêu chuẩn, và bạn có thể chắc chắn mã của bạn sẽ biên dịch và chạy không thay đổi trên cả hai hệ thống ngày nay và ngày mai.

Mặc dù thẳng thắn, trừ khi bạn là một chuyên gia và làm việc trên một số mã cấp thấp nghiêm trọng, bạn có lẽ nên dính vào mutexes và biến điều kiện. Đó là những gì tôi định làm.

Để biết thêm về nội dung này, hãy xem bài đăng trên blog này.


1803
2018-06-12 00:23



Câu trả lời hay, nhưng điều này thực sự là cầu xin cho một số ví dụ thực tế của các nguyên thủy mới. Ngoài ra, tôi nghĩ rằng bộ nhớ đặt hàng mà không có nguyên thủy là giống như pre-C ++ 0x: không có đảm bảo. - John Ripley
@ John: Tôi biết, nhưng tôi vẫn đang học các nguyên thủy bản thân mình :-). Ngoài ra tôi nghĩ rằng họ đảm bảo truy cập byte là nguyên tử (mặc dù không ra lệnh) đó là lý do tại sao tôi đã đi với "char" cho ví dụ của tôi ... Nhưng tôi thậm chí không chắc chắn 100% về điều đó ... Nếu bạn muốn đề nghị bất kỳ tốt " hướng dẫn "tài liệu tham khảo tôi sẽ thêm chúng vào câu trả lời của tôi - Nemo
@Nawaz: Vâng! Truy cập bộ nhớ có thể được sắp xếp lại bởi trình biên dịch hoặc CPU. Hãy suy nghĩ về (ví dụ) lưu trữ và tải trọng đầu cơ. Thứ tự mà bộ nhớ hệ thống bị tấn công có thể không giống với thứ bạn đã mã hóa. Trình biên dịch và CPU sẽ đảm bảo sắp xếp lại như vậy không phá vỡ đơn luồng mã. Đối với mã đa luồng, "mô hình bộ nhớ" mô tả các thứ tự sắp xếp lại có thể và điều gì sẽ xảy ra nếu hai luồng đọc / ghi cùng một vị trí cùng một lúc và cách bạn kiểm soát cả hai. Đối với mã đơn luồng, mô hình bộ nhớ không liên quan. - Nemo
@Nawaz, @Nemo - Một chi tiết nhỏ: mô hình bộ nhớ mới có liên quan trong mã đơn luồng trong khi nó chỉ định tính không xác định của các biểu thức nhất định, chẳng hạn như i = i++. Khái niệm cũ về điểm chuỗi đã bị loại bỏ; tiêu chuẩn mới chỉ định điều tương tự bằng cách sử dụng được sắp xếp trước mối quan hệ đó chỉ là trường hợp đặc biệt của chủ đề liên kết chung hơn xảy ra trước khái niệm. - JohannesD
@ AJG85: Phần 3.6.2 của dự thảo C ++ 0x spec nói, "Các biến với thời gian lưu trữ tĩnh (3.7.1) hoặc thời gian lưu trữ luồng (3.7.2) sẽ được khởi tạo bằng không (8.5) trước khi bất kỳ khởi tạo nào khác mất địa điểm." Vì x, y là toàn cầu trong ví dụ này, chúng có thời gian lưu trữ tĩnh và do đó sẽ không khởi tạo, tôi tin. - Nemo


Tôi sẽ chỉ cung cấp cho các tương tự mà tôi hiểu mô hình nhất quán bộ nhớ (hoặc các mô hình bộ nhớ, cho ngắn). Nó được lấy cảm hứng từ giấy tinh hoa của Leslie Lamport "Thời gian, Đồng hồ và thứ tự sự kiện trong hệ thống phân tán". Sự tương tự là apt và có ý nghĩa cơ bản, nhưng có thể là quá mức cần thiết cho nhiều người. Tuy nhiên, tôi hy vọng nó cung cấp một hình ảnh tinh thần (một hình ảnh đại diện) tạo điều kiện lý luận về các mô hình nhất quán bộ nhớ.

Hãy xem lịch sử của tất cả các vị trí bộ nhớ trong biểu đồ không gian trong đó trục hoành biểu diễn không gian địa chỉ (tức là mỗi vị trí bộ nhớ được biểu diễn bằng một điểm trên trục đó) và trục dọc biểu thị thời gian (chúng ta sẽ thấy rằng, nói chung, không có khái niệm chung về thời gian). Do đó, lịch sử của các giá trị được giữ bởi mỗi vị trí bộ nhớ được biểu thị bằng một cột dọc tại địa chỉ bộ nhớ đó. Mỗi thay đổi giá trị là do một trong các chủ đề viết một giá trị mới cho vị trí đó. Bởi một hình ảnh bộ nhớ, chúng tôi sẽ có nghĩa là tổng hợp / kết hợp các giá trị của tất cả các vị trí bộ nhớ có thể quan sát được tại một thời điểm cụ thể bởi một chủ đề cụ thể.

Trích dẫn từ "Một Primer về tính nhất quán của bộ nhớ và sự liên kết bộ nhớ cache"

Mô hình bộ nhớ trực quan (và hạn chế nhất) là tính nhất quán tuần tự (SC) trong đó một thực thi đa luồng sẽ giống như một sự xen kẽ các thực thi tuần tự của mỗi chuỗi thành phần, như thể các chuỗi được ghép thời gian trên một bộ xử lý lõi đơn.

Thứ tự bộ nhớ toàn cục có thể thay đổi từ một lần chạy chương trình này sang chương trình khác và có thể không được biết trước. Tính năng đặc trưng của SC là tập hợp các lát nằm ngang trong sơ đồ không gian địa chỉ thời gian biểu diễn -máy bay đồng thời (ví dụ: hình ảnh bộ nhớ). Trên một mặt phẳng nhất định, tất cả các sự kiện của nó (hoặc các giá trị bộ nhớ) đều đồng thời. Có một khái niệm về Thời gian tuyệt đối, trong đó tất cả các chủ đề đồng ý về giá trị bộ nhớ nào đồng thời. Trong SC, tại mọi thời điểm, chỉ có một hình ảnh bộ nhớ được chia sẻ bởi tất cả các chuỗi. Tức là, vào mọi thời điểm, tất cả các bộ vi xử lý đều đồng ý về hình ảnh bộ nhớ (tức là, nội dung tổng hợp của bộ nhớ). Điều này không chỉ ngụ ý rằng tất cả các luồng đều xem cùng một chuỗi các giá trị cho tất cả các vị trí bộ nhớ, mà còn tất cả các bộ xử lý đều quan sát kết hợp các giá trị của tất cả các biến. Điều này cũng giống như nói rằng tất cả các hoạt động bộ nhớ (trên tất cả các vị trí bộ nhớ) được quan sát trong cùng một tổng số thứ tự của tất cả các chủ đề.

Trong các mô hình bộ nhớ thoải mái, mỗi luồng sẽ cắt không gian địa chỉ theo cách riêng của nó, hạn chế duy nhất là các lát của mỗi luồng sẽ không giao nhau vì tất cả các luồng phải đồng ý về lịch sử của từng vị trí bộ nhớ riêng lẻ (tất nhiên , lát của các chủ đề khác nhau có thể, và sẽ, qua nhau). Không có cách phổ biến nào để cắt nó ra (không có đặc quyền của địa chỉ không-thời gian). Các lát không cần phải phẳng (hoặc tuyến tính). Chúng có thể được uốn cong và đây là những gì có thể làm cho một chuỗi đọc các giá trị được viết bởi một luồng khác theo thứ tự chúng được viết. khi được xem bởi bất kỳ chủ đề cụ thể nào. Mỗi luồng sẽ có ý nghĩa khác nhau về các sự kiện (hoặc, tương đương, các giá trị bộ nhớ) đồng thời. Tập hợp các sự kiện (hoặc các giá trị bộ nhớ) đồng thời với một luồng không đồng thời với một chuỗi khác. Vì vậy, trong một mô hình bộ nhớ thoải mái, tất cả các chủ đề vẫn quan sát cùng một lịch sử (tức là, chuỗi các giá trị) cho mỗi vị trí bộ nhớ. Nhưng chúng có thể quan sát các hình ảnh bộ nhớ khác nhau (tức là, sự kết hợp các giá trị của tất cả các vị trí bộ nhớ). Ngay cả khi hai vị trí bộ nhớ khác nhau được viết bởi cùng một chuỗi theo thứ tự, hai giá trị mới được viết có thể được quan sát theo thứ tự khác nhau bởi các luồng khác.

[Hình ảnh từ Wikipedia] Picture from Wikipedia

Độc giả quen thuộc với Einstein Lý thuyết tương đối đặc biệt sẽ nhận thấy những gì tôi đang ám chỉ. Dịch các từ của Minkowski thành lĩnh vực mô hình bộ nhớ: không gian địa chỉ và thời gian là các vùng không gian địa chỉ. Trong trường hợp này, mỗi người quan sát (nghĩa là luồng) sẽ chiếu bóng của các sự kiện (ví dụ, lưu trữ bộ nhớ / tải) vào dòng thế giới của riêng mình (tức là trục thời gian của anh) và mặt phẳng đồng thời của riêng mình (trục không gian địa chỉ của anh) . Các chủ đề trong mô hình bộ nhớ C ++ 11 tương ứng với người quan sát đang di chuyển tương đối với nhau trong thuyết tương đối hẹp. Tính nhất quán tuần tự tương ứng với Galilean không gian-thời gian (tức là, tất cả các nhà quan sát đồng ý về một thứ tự tuyệt đối của các sự kiện và ý thức toàn cầu về tính đồng thời).

Sự tương đồng giữa các mô hình bộ nhớ và thuyết tương đối hẹp bắt nguồn từ thực tế là cả hai xác định một tập hợp các sự kiện được sắp xếp một phần, thường được gọi là tập hợp nhân quả. Một số sự kiện (tức là, các cửa hàng bộ nhớ) có thể ảnh hưởng (nhưng không bị ảnh hưởng bởi) các sự kiện khác. Chủ đề C ++ 11 (hoặc người quan sát trong vật lý) không nhiều hơn một chuỗi (tức là, một tập hợp được sắp xếp hoàn toàn) của sự kiện (ví dụ: tải bộ nhớ và lưu trữ đến các địa chỉ khác nhau).

Trong thuyết tương đối, một số trật tự được khôi phục về hình ảnh có vẻ hỗn loạn của các sự kiện được sắp xếp một phần, vì thứ tự thời gian duy nhất mà tất cả các nhà quan sát đồng ý là sự sắp đặt giữa các sự kiện “thời gian” (tức là, những sự kiện đó về nguyên tắc có thể kết nối với bất kỳ hạt nào đi chậm hơn hơn tốc độ ánh sáng trong chân không). Chỉ những sự kiện liên quan đến thời gian mới được đặt hàng bất biến. Thời gian trong Vật lý, Craig Callender.

Trong mô hình bộ nhớ C ++ 11, một cơ chế tương tự (mô hình nhất quán phát hành có được) được sử dụng để thiết lập các quan hệ nhân quả địa phương.

Để cung cấp một định nghĩa về tính nhất quán của bộ nhớ và động lực để từ bỏ SC, tôi sẽ trích dẫn từ "Một Primer về tính nhất quán của bộ nhớ và sự liên kết bộ nhớ cache"

Đối với một máy tính chia sẻ bộ nhớ, mô hình nhất quán bộ nhớ xác định hành vi có thể nhìn thấy kiến ​​trúc của hệ thống bộ nhớ của nó. Tiêu chuẩn chính xác cho một hành vi phân vùng lõi bộ vi xử lý duy nhất giữa “một kết quả chính xác”Và“nhiều lựa chọn thay thế không chính xác”. Điều này là do kiến ​​trúc của bộ vi xử lý yêu cầu rằng việc thực thi một luồng biến đổi một trạng thái đầu vào đã cho thành một trạng thái đầu ra được xác định tốt, ngay cả trên một lõi không theo thứ tự. Tuy nhiên, các mô hình nhất quán bộ nhớ dùng chung liên quan đến tải và lưu trữ nhiều luồng và thường cho phép nhiều lần thực hiện đúng trong khi không cho phép nhiều (nhiều hơn) không chính xác. Khả năng thực thi nhiều lần chính xác là do ISA cho phép nhiều luồng thực hiện đồng thời, thường với nhiều sự xen kẽ hợp pháp có thể có của các lệnh từ các luồng khác nhau.

Thư giãn hoặc là Yếu mô hình bộ nhớ nhất quán được thúc đẩy bởi thực tế là hầu hết các thứ tự bộ nhớ trong các mô hình mạnh là không cần thiết. Nếu một luồng cập nhật mười mục dữ liệu và sau đó là cờ đồng bộ, người lập trình thường không quan tâm nếu các mục dữ liệu được cập nhật theo thứ tự với nhau nhưng chỉ tất cả các mục dữ liệu được cập nhật trước khi cờ được cập nhật (thường được thực hiện bằng hướng dẫn FENCE) ). Các mô hình thư giãn tìm cách nắm bắt sự linh hoạt khi đặt hàng tăng lên này và chỉ duy trì các đơn đặt hàng mà các lập trình viên “yêu cầu”Để có được hiệu suất và độ chính xác cao hơn của SC. Ví dụ, trong một số kiến ​​trúc, bộ đệm ghi FIFO được sử dụng bởi mỗi lõi để giữ kết quả của các cửa hàng đã cam kết (đã nghỉ hưu) trước khi ghi kết quả vào bộ đệm. Tối ưu hóa này tăng cường hiệu suất nhưng vi phạm SC. Bộ đệm ghi ẩn độ trễ của việc phục vụ một cửa hàng bỏ lỡ. Bởi vì các cửa hàng là phổ biến, có thể tránh bị trì hoãn trên hầu hết trong số họ là một lợi ích quan trọng. Đối với một bộ xử lý lõi đơn, bộ đệm ghi có thể được thực hiện theo kiến ​​trúc vô hình bằng cách đảm bảo rằng một tải đến địa chỉ A trả về giá trị của cửa hàng gần đây nhất cho A ngay cả khi một hoặc nhiều cửa hàng đến A nằm trong bộ đệm ghi. Điều này thường được thực hiện bằng cách bỏ qua giá trị của cửa hàng gần đây nhất đến A đến tải từ A, trong đó "gần đây nhất" được xác định theo thứ tự chương trình, hoặc bằng cách trì hoãn tải A nếu một cửa hàng A nằm trong bộ đệm ghi . Khi nhiều lõi được sử dụng, mỗi lõi sẽ có bộ đệm ghi bỏ qua riêng của nó. Nếu không có bộ đệm ghi, phần cứng là SC, nhưng với bộ đệm ghi, nó không phải là, làm cho bộ đệm ghi kiến ​​trúc có thể nhìn thấy trong một bộ xử lý đa lõi.

Sắp xếp lại cửa hàng lưu trữ có thể xảy ra nếu lõi có bộ đệm ghi không phải FIFO cho phép các cửa hàng khởi hành theo thứ tự khác với thứ tự mà chúng đã nhập. Điều này có thể xảy ra nếu cửa hàng đầu tiên bị thiếu trong bộ nhớ cache trong khi lần truy cập thứ hai hoặc nếu cửa hàng thứ hai có thể kết hợp với cửa hàng trước đó (tức là trước cửa hàng đầu tiên). Việc sắp xếp lại tải cũng có thể xảy ra trên các lõi được lập lịch động, thực hiện các lệnh từ thứ tự chương trình. Điều đó có thể hoạt động giống như sắp xếp lại các cửa hàng trên lõi khác (Bạn có thể đưa ra một ví dụ xen kẽ giữa hai luồng không?). Sắp xếp lại một tải trước đó với một cửa hàng sau (sắp xếp lại cửa hàng tải) có thể gây ra nhiều hành vi không chính xác, chẳng hạn như tải một giá trị sau khi nhả khóa bảo vệ nó (nếu cửa hàng là hoạt động mở khóa). Lưu ý rằng việc sắp xếp lại tải cửa hàng cũng có thể phát sinh do việc bỏ qua cục bộ trong bộ đệm ghi FIFO thường được triển khai, ngay cả với một lõi thực thi tất cả các lệnh trong thứ tự chương trình.

Bởi vì sự kết hợp bộ nhớ cache và tính nhất quán của bộ nhớ đôi khi bị nhầm lẫn, nên cũng có hướng dẫn để có câu trích dẫn sau:

Không giống như tính nhất quán, sự kết hợp bộ nhớ cache không hiển thị với phần mềm cũng như yêu cầu. Coherence tìm cách làm cho cache của một hệ thống chia sẻ bộ nhớ có chức năng vô hình như các cache trong một hệ thống đơn lõi. Sự kết hợp chính xác đảm bảo rằng một lập trình viên không thể xác định liệu hệ thống có bộ nhớ cache hay không bằng cách phân tích kết quả của các tải và các cửa hàng. Điều này là do sự kết hợp chính xác đảm bảo rằng bộ nhớ cache không bao giờ cho phép mới hoặc khác chức năng hành vi (lập trình viên vẫn có thể suy ra khả năng cấu trúc bộ nhớ cache bằng cách sử dụng thời gian thông tin). Mục đích chính của các giao thức kết hợp bộ nhớ cache là duy trì bất biến một người viết-nhiều độc giả (SWMR) cho mọi vị trí bộ nhớ.   Một sự khác biệt quan trọng giữa sự mạch lạc và nhất quán là sự gắn kết được xác định trên cơ sở vị trí trên mỗi bộ nhớ, trong khi tính nhất quán được quy định đối với tất cả các vị trí bộ nhớ.

Tiếp tục với hình ảnh tinh thần của chúng tôi, bất biến SWMR tương ứng với yêu cầu vật lý có tối đa một hạt nằm ở một vị trí bất kỳ nhưng có thể có số lượng quan sát không giới hạn ở bất kỳ vị trí nào.


281
2017-08-29 20:42



1 cho tương tự với thuyết tương đối hẹp, tôi đã cố gắng tự mình làm tương tự. Tôi thường thấy các lập trình viên điều tra mã luồng cố gắng giải thích hành vi khi các hoạt động trong các luồng khác nhau xuất hiện xen kẽ với nhau theo thứ tự cụ thể, và tôi phải nói với họ rằng, với các hệ thống đa xử lý, khái niệm đồng thời giữa các <s > khung tham chiếu </ s> chủ đề bây giờ là vô nghĩa. So sánh với thuyết tương đối hẹp là một cách tốt để làm cho chúng tôn trọng sự phức tạp của vấn đề. - Pierre Lebeaupin
@Ahmed Nassar: liên kết bạn chia sẻ từ stanford đã chết. - Joze
@ Joze: Cảm ơn. Tôi đã đề cập đến thư viện ACM. Nó vẫn có sẵn miễn phí ở nơi khác trên Web. - Ahmed Nassar
Vì vậy, bạn nên kết luận rằng vũ trụ là đa lõi? - Peter K
@PeterK: Chính xác :) Và đây là một hình ảnh rất đẹp về hình ảnh thời gian này của nhà vật lý Brian Greene: youtube.com/watch?v=4BjGWLJNPcA&t=22m12s  Đây là "Illusion of Time [Full Documentary]" ở phút 22 và 12 giây. - Ahmed Nassar


Đây là một câu hỏi cũ nhiều năm, nhưng rất phổ biến, nó đáng nói đến một nguồn tài nguyên tuyệt vời để tìm hiểu về mô hình bộ nhớ C ++ 11. Tôi thấy không có điểm trong tổng kết cuộc nói chuyện của mình để làm cho câu trả lời này chưa đầy đủ, nhưng vì đây là người thực sự đã viết tiêu chuẩn, tôi nghĩ rằng nó cũng có giá trị xem các cuộc nói chuyện.

Herb Sutter có một cuộc nói chuyện dài ba giờ về mô hình bộ nhớ C ++ 11 có tiêu đề "nguyên tử <> Vũ khí", có sẵn trên trang web Channel9 - phần 1 và phần 2. Bài nói chuyện khá kỹ thuật và bao gồm các chủ đề sau:

  1. Tối ưu hóa, chủng tộc và mô hình bộ nhớ
  2. Đặt hàng - Điều gì: Có được và phát hành
  3. Đặt hàng - Cách thực hiện: Mutexes, Atomics và / hoặc hàng rào
  4. Các hạn chế khác trên trình biên dịch và phần cứng
  5. Mã & hiệu suất: x86 / x64, IA64, POWER, ARM
  6. Atomic thư giãn

Cuộc nói chuyện không phức tạp trên API, mà là trên lý luận, nền, dưới mui xe và đằng sau hậu trường (bạn có biết ngữ nghĩa thoải mái đã được thêm vào tiêu chuẩn chỉ vì POWER và ARM không hỗ trợ tải đồng bộ một cách hiệu quả?).


80
2017-12-20 13:22



Cuộc nói chuyện đó thực sự tuyệt vời, hoàn toàn đáng giá trong 3 giờ bạn sẽ dành để xem nó. - ZunTzu
@ ZunTzu: trên hầu hết các trình phát video, bạn có thể đặt tốc độ thành 1.25, 1.5 hoặc thậm chí 2 lần so với bản gốc. - Christian Severin
@eran các bạn có tình cờ có các slide không? các liên kết trên các trang thảo luận của kênh 9 không hoạt động. - athos
@ Tôi không có họ, xin lỗi. Hãy thử liên hệ với kênh 9, tôi không nghĩ rằng việc xóa đã được cố ý (tôi đoán là họ có liên kết từ Herb Sutter, được đăng lên và sau đó anh ấy đã xóa các tệp đó, nhưng đó chỉ là một suy đoán ...). - eran


Nó có nghĩa là tiêu chuẩn bây giờ định nghĩa đa luồng, và nó định nghĩa những gì xảy ra trong bối cảnh của nhiều luồng. Tất nhiên, mọi người đã sử dụng các cách triển khai khác nhau, nhưng điều đó giống như hỏi tại sao chúng ta nên có std::string khi tất cả chúng ta có thể sử dụng nhà cuộn string lớp học.

Khi bạn đang nói về các chủ đề POSIX hoặc các chủ đề Windows, thì đây là một chút ảo giác khi bạn đang nói về các chủ đề x86, vì nó là một hàm phần cứng để chạy đồng thời. Mô hình bộ nhớ C ++ 0x đảm bảo sự bảo đảm, cho dù bạn đang sử dụng x86 hay ARM hay MIPS, hoặc bất cứ điều gì khác bạn có thể đến với.


68
2018-06-11 23:42



Chủ đề Posix không bị giới hạn ở x86. Thật vậy, các hệ thống đầu tiên chúng được triển khai trên có lẽ không phải là hệ thống x86. Các chuỗi Posix là hệ thống độc lập và có giá trị trên tất cả các nền tảng Posix. Nó cũng không thực sự đúng rằng đó là một tài sản phần cứng bởi vì chủ đề Posix cũng có thể được thực hiện thông qua đa nhiệm hợp tác xã. Nhưng tất nhiên, hầu hết các vấn đề luồng chỉ xuất hiện trên các triển khai luồng phần cứng (và một số thậm chí chỉ trên các hệ thống đa xử lý / đa lõi). - celtschk


Đối với các ngôn ngữ không chỉ định một mô hình bộ nhớ, bạn đang viết mã cho ngôn ngữ  mô hình bộ nhớ được chỉ định bởi kiến ​​trúc bộ vi xử lý. Bộ xử lý có thể chọn sắp xếp lại các truy cập bộ nhớ để thực hiện. Vì thế, nếu chương trình của bạn có các cuộc đua dữ liệu (một cuộc đua dữ liệu là khi nó có thể cho nhiều lõi / siêu chủ đề để truy cập cùng một bộ nhớ đồng thời) thì chương trình của bạn không phải là nền tảng chéo vì sự phụ thuộc vào mô hình bộ nhớ của bộ xử lý. Bạn có thể tham khảo sách hướng dẫn sử dụng phần mềm Intel hoặc AMD để tìm hiểu cách thức các bộ vi xử lý có thể sắp xếp lại thứ tự bộ nhớ.

Rất quan trọng, khóa (và ngữ nghĩa đồng thời với khóa) thường được thực hiện theo một cách nền tảng chéo ... Vì vậy, nếu bạn đang sử dụng khóa tiêu chuẩn trong một chương trình đa luồng mà không có cuộc đua dữ liệu thì bạn không phải lo lắng về các mô hình bộ nhớ nền tảng chéo.

Thật thú vị, các trình biên dịch của Microsoft cho C ++ đã có được / phát hành ngữ nghĩa cho biến động, đó là một phần mở rộng C ++ để giải quyết việc thiếu một mô hình bộ nhớ trong C ++ http://msdn.microsoft.com/en-us/library/12a04hfd(v=vs.80).aspx. Tuy nhiên, với điều kiện Windows chỉ chạy trên x86 / x64, điều đó không nói nhiều (các mô hình bộ nhớ của Intel và AMD giúp dễ dàng và hiệu quả để triển khai các ngữ nghĩa thu nhận / giải phóng trong một ngôn ngữ).


49
2017-07-26 04:27



Đúng là, khi câu trả lời được viết, Windows chỉ chạy trên x86 / x64, nhưng Windows chạy, tại một thời điểm nào đó, trên IA64, MIPS, Alpha AXP64, PowerPC và ARM. Hôm nay nó chạy trên các phiên bản khác nhau của ARM, đó là bộ nhớ khá khác nhau khôn ngoan từ x86, và hư không gần như là tha thứ. - Lorenzo Dematté
Liên kết đó phần nào bị hỏng (nói "Visual Studio 2005 Tài liệu về hưu"). Chăm sóc để cập nhật nó? - Peter Mortensen
Nó không đúng ngay cả khi câu trả lời được viết. - Ben
"để truy cập cùng một bộ nhớ đồng thời"để truy cập trong một mâu thuẫn đường - curiousguy


Nếu bạn sử dụng mutexes để bảo vệ tất cả dữ liệu của bạn, bạn thực sự không cần phải lo lắng. Mutexes luôn cung cấp bảo đảm trật tự và khả năng hiển thị đầy đủ.

Bây giờ, nếu bạn sử dụng nguyên tử hoặc thuật toán không có khóa, bạn cần phải suy nghĩ về mô hình bộ nhớ. Mô hình bộ nhớ mô tả chính xác khi các nguyên tử cung cấp bảo đảm trật tự và khả năng hiển thị và cung cấp hàng rào di động cho các đảm bảo mã hóa bằng tay.

Trước đây, nguyên tử sẽ được thực hiện bằng cách sử dụng trình biên dịch nội tại, hoặc một số thư viện cấp cao hơn. Hàng rào sẽ được thực hiện bằng cách sử dụng các lệnh cụ thể cho CPU (các rào cản bộ nhớ).


22
2018-06-11 23:49



Vấn đề trước đó là không có thứ như một mutex (theo tiêu chuẩn C ++). Vì vậy, sự đảm bảo duy nhất bạn được cung cấp là do nhà sản xuất mutex, điều này cũng miễn là bạn không chuyển mã (vì những thay đổi nhỏ về bảo đảm khó phát hiện). Bây giờ chúng tôi được đảm bảo cung cấp bởi các tiêu chuẩn mà nên được di động giữa các nền tảng. - Martin York
@ Martin: trong mọi trường hợp, một điều là mô hình bộ nhớ, và một là nguyên tử và luồng nguyên thủy chạy trên đầu trang của mô hình bộ nhớ đó. - ninjalj
Ngoài ra, quan điểm của tôi chủ yếu là trước đây hầu như không có mô hình bộ nhớ ở cấp độ ngôn ngữ, nó xảy ra là mô hình bộ nhớ của CPU cơ bản. Bây giờ có một mô hình bộ nhớ là một phần của ngôn ngữ cốt lõi; OTOH, mutexes và những thứ tương tự luôn có thể được thực hiện như một thư viện. - ninjalj
Nó cũng có thể là một vấn đề thực sự cho những người đang cố gắng viết thư viện mutex. Khi CPU, bộ điều khiển bộ nhớ, hạt nhân, trình biên dịch, và "thư viện C" đều được thực hiện bởi các nhóm khác nhau, và một số trong chúng bất đồng bạo lực như thế nào công cụ này có nghĩa vụ phải làm việc, tốt, đôi khi các công cụ các lập trình viên hệ thống của chúng tôi phải làm gì để trình bày một mặt tiền đẹp đến mức ứng dụng không hề dễ chịu chút nào. - zwol
Thật không may là không đủ để bảo vệ cấu trúc dữ liệu của bạn với các mutex đơn giản nếu không có một mô hình bộ nhớ nhất quán trong ngôn ngữ của bạn. Có tối ưu hóa trình biên dịch khác nhau có ý nghĩa trong một bối cảnh luồng đơn nhưng khi nhiều luồng và lõi CPU đi vào hoạt động, sắp xếp lại quyền truy cập bộ nhớ và tối ưu hóa khác có thể mang lại hành vi không xác định. Để biết thêm thông tin, xem "Chủ đề không thể được thực hiện như một thư viện" của Hans Boehm: citeseer.ist.psu.edu/viewdoc/… - exDM69