Khi các tính toán giới hạn băng thông bộ nhớ được thực hiện trong môi trường bộ nhớ dùng chung (ví dụ: luồng qua OpenMP, Pthreads hoặc TBB), có một vấn đề nan giải là làm thế nào để đảm bảo rằng bộ nhớ được phân phối chính xác trên bộ nhớ vật lý , sao cho mỗi luồng chủ yếu truy cập bộ nhớ trên một Xe buýt bộ nhớ "cục bộ". Mặc dù các giao diện không khả dụng, nhưng hầu hết các hệ điều hành đều có cách đặt mối quan hệ luồng (ví dụ: pthread_setaffinity_np()
trên nhiều hệ thống POSIX, sched_setaffinity()
trên Linux, SetThreadAffinityMask()
trên Windows). Ngoài ra còn có các thư viện như hwloc để xác định hệ thống phân cấp bộ nhớ, nhưng thật không may, hầu hết các hệ điều hành chưa cung cấp cách để đặt chính sách bộ nhớ NUMA. Linux là một ngoại lệ đáng chú ý, với libnumacho phép ứng dụng thao tác chính sách bộ nhớ và di chuyển trang ở mức độ chi tiết của trang (theo dòng chính từ năm 2004, do đó có sẵn rộng rãi). Các hệ điều hành khác mong muốn người dùng tuân thủ chính sách "chạm đầu tiên" ngầm định.
Làm việc với chính sách "chạm đầu tiên" có nghĩa là người gọi nên tạo và phân phối các luồng với bất kỳ mối quan hệ nào họ dự định sử dụng sau này khi lần đầu tiên ghi vào bộ nhớ được cấp phát mới. (Rất ít hệ thống được cấu hình sao cho malloc()
thực sự tìm thấy các trang, nó chỉ hứa sẽ tìm thấy chúng khi chúng thực sự bị lỗi, có lẽ bởi các luồng khác nhau.) Điều này ngụ ý rằng việc cấp phát sử dụng calloc()
hoặc khởi tạo ngay bộ nhớ sau khi phân bổ sử dụng memset()
là có hại vì nó sẽ có lỗi tất cả bộ nhớ trên bus bộ nhớ của lõi chạy luồng cấp phát, dẫn đến băng thông bộ nhớ trong trường hợp xấu nhất khi bộ nhớ được truy cập từ nhiều luồng. Điều tương tự cũng áp dụng cho new
toán tử C ++ , đòi hỏi phải khởi tạo nhiều phân bổ mới (ví dụ:std::complex
). Một số quan sát về môi trường này:
- Phân bổ có thể được tạo thành "tập thể luồng", nhưng giờ phân bổ trở thành hỗn hợp vào mô hình luồng, điều không mong muốn đối với các thư viện có thể phải tương tác với các máy khách bằng cách sử dụng các mô hình luồng khác nhau (có lẽ mỗi nhóm có nhóm luồng riêng).
- RAII được coi là một phần quan trọng của C ++ thành ngữ, nhưng dường như nó có hại tích cực cho hiệu năng bộ nhớ trong môi trường NUMA. Vị trí
new
có thể được sử dụng với bộ nhớ được phân bổ thông quamalloc()
hoặc thường xuyên từlibnuma
, nhưng điều này thay đổi quá trình phân bổ (mà tôi tin là cần thiết). - EDIT: Tuyên bố trước đây của tôi về toán tử
new
là không chính xác, nó có thể hỗ trợ nhiều đối số, xem câu trả lời của Chetan. Tôi tin rằng vẫn còn lo ngại về việc thư viện hoặc bộ chứa STL sử dụng mối quan hệ được chỉ định. Nhiều trường có thể được đóng gói và có thể bất tiện để đảm bảo rằng, ví dụ, mộtstd::vector
phân bổ lại với trình quản lý bối cảnh chính xác đang hoạt động. - Mỗi luồng có thể phân bổ và lỗi bộ nhớ riêng của nó, nhưng sau đó lập chỉ mục vào các vùng lân cận thì phức tạp hơn. (Xem xét một sản phẩm vectơ ma trận thưa thớt với phân vùng hàng của ma trận và vectơ; lập chỉ mục phần chưa được đặt của x yêu cầu cấu trúc dữ liệu phức tạp hơn khi x không liền kề trong bộ nhớ ảo.)
Có bất kỳ giải pháp nào để phân bổ / khởi tạo NUMA được coi là thành ngữ không? Tôi đã bỏ đi các vấn đề quan trọng khác chưa?
(Tôi không có ý cho tôi C ++ ví dụ để ngụ ý một sự nhấn mạnh về ngôn ngữ đó, tuy nhiên C ++ ngôn ngữ mã hóa một số quyết định về quản lý bộ nhớ rằng một ngôn ngữ như C không, do đó có xu hướng có sức đề kháng hơn khi gợi ý rằng C lập trình viên ++ làm những những thứ khác nhau.)