Chia sẻ sai giữa các chuỗi là khi 2 hoặc nhiều chủ đề sử dụng cùng một dòng bộ nhớ cache.
Ví dụ: :
struct Work {
Work(int& d) : data(d) {}
void operator()() {
++data;
}
int& data;
};
int main() {
int false_sharing[10] = { 0 };
boost::thread_group threads;
for (int i = 0; i < 10; ++i)
threads.create_thread(Work(false_sharing[i]));
threads.join_all();
int no_false_sharing[10 * CACHELINE_SIZE_INTS] = { 0 };
for (int i = 0; i < 10; ++i)
threads.create_thread(Work(no_false_sharing[i * CACHELINE_SIZE_INTS]));
threads.join_all();
}
Chủ đề trong khối đầu tiên bị chia sẻ sai. Các chủ đề trong khối thứ hai không (nhờ CACHELINE_SIZE
).
Dữ liệu trên ngăn xếp luôn cách 'xa' các chủ đề khác. (Ví dụ: dưới cửa sổ, ít nhất một vài trang).
Với định nghĩa của đối tượng hàm, chia sẻ sai có thể xuất hiện, vì các trường hợp Work
được tạo trên vùng heap và không gian vùng này được sử dụng bên trong chuỗi.
Điều này có thể dẫn đến một số trường hợp Work
liền kề và do đó có thể phải chia sẻ các dòng bộ nhớ cache.
Nhưng ... mẫu của bạn không có ý nghĩa, bởi vì dữ liệu không bao giờ được chạm vào bên ngoài và do đó chia sẻ sai được gây ra không cần thiết.
Cách dễ nhất, để ngăn chặn các vấn đề như thế này, là sao chép dữ liệu 'được chia sẻ' cục bộ trên tho ngăn xếp và sau đó làm việc trên bản sao ngăn xếp. Khi công việc của bạn hoàn tất, hãy sao chép nó trở lại đầu ra var.
ví dụ:
struct Work {
Work(int& d) : data(d) {}
void operator()()
{
int tmp = data;
for(int i = 0; i < lengthy_op; ++i)
++tmp;
data = tmp;
}
int& data;
};
Điều này ngăn cản tất cả các vấn đề với chia sẻ.
Mã sẽ hoạt động tốt hơn. Nếu các đối tượng hàm của bạn có dữ liệu 'tĩnh', thì tất cả các luồng sẽ chia sẻ dữ liệu đó. – GManNickG
Hãy suy nghĩ bạn cần phải nói chính xác những gì bạn có nghĩa là "mỗi thread được bản sao riêng của nó" và "phân bổ tĩnh". Các chủ đề có sử dụng lẫn nhau không? – Elemental
@Elemental: Một số trình biên dịch có thể sử dụng bộ nhớ cục bộ TLS-thread. Điều này có nghĩa rằng bạn có thể phân bổ tĩnh và thread-an toàn, mặc dù như vậy là chậm. – Puppy