Bạn chắc chắn đang nhận được những gì tôi gọi là cộng hưởng bộ nhớ cache . Điều này tương tự với răng cưa , nhưng không hoàn toàn giống nhau. Hãy để tôi giải thích.
Bộ nhớ đệm là cấu trúc dữ liệu phần cứng trích xuất một phần của địa chỉ và sử dụng nó làm chỉ mục trong bảng, không giống như một mảng trong phần mềm. (Trên thực tế, chúng tôi gọi chúng là mảng trong phần cứng.) Mảng bộ nhớ cache chứa các dòng dữ liệu và thẻ trong bộ nhớ cache - đôi khi một mục nhập như vậy cho mỗi chỉ mục trong mảng (được ánh xạ trực tiếp), đôi khi là một số như vậy (liên kết tập N-way). Phần thứ hai của địa chỉ được trích xuất và so sánh với thẻ được lưu trữ trong mảng. Cùng với nhau, chỉ mục và thẻ xác định duy nhất một địa chỉ bộ nhớ dòng bộ nhớ cache. Cuối cùng, phần còn lại của các bit địa chỉ xác định các byte nào trong dòng bộ đệm được địa chỉ hóa, cùng với kích thước của quyền truy cập.
Thông thường chỉ mục và thẻ là các trường bit đơn giản. Vì vậy, một địa chỉ bộ nhớ trông giống như
...Tag... | ...Index... | Offset_within_Cache_Line
(Đôi khi chỉ mục và thẻ là các hàm băm, ví dụ như một vài XOR của các bit khác vào các bit tầm trung là chỉ mục. Hiếm khi hơn, đôi khi là chỉ mục và hiếm hơn là thẻ, là những thứ như lấy mô-đun địa chỉ dòng bộ nhớ cache a số nguyên tố. Các phép tính chỉ số phức tạp hơn này là nỗ lực để chống lại vấn đề cộng hưởng, mà tôi giải thích ở đây. Tất cả đều phải chịu một số dạng cộng hưởng, nhưng các sơ đồ trích xuất trường bit đơn giản nhất bị cộng hưởng trên các mẫu truy cập phổ biến, như bạn đã tìm thấy.)
Vì vậy, các giá trị điển hình ... có nhiều mô hình khác nhau của "Opteron Dual Core", và tôi không thấy bất kỳ điều gì ở đây chỉ định bạn có cái nào. Chọn một cách ngẫu nhiên, hướng dẫn sử dụng gần đây nhất mà tôi thấy trên trang web của AMD, Hướng dẫn của nhà phát triển Bios và Kernel (BKDG) cho các kiểu máy AMD Family 15h 00h-0Fh , ngày 12 tháng 3 năm 2012.
(Family 15h = Bulldozer family, bộ vi xử lý cao cấp gần đây nhất - BKDG đề cập đến lõi kép, mặc dù tôi không biết số sản phẩm chính xác như những gì bạn mô tả. Nhưng dù sao, ý tưởng cộng hưởng giống nhau áp dụng cho tất cả các bộ xử lý, chỉ là các tham số như kích thước bộ nhớ cache và tính liên kết có thể thay đổi một chút.)
Từ tr.33:
Bộ xử lý AMD Family 15h chứa bộ nhớ đệm dữ liệu L1 dự đoán 16-Kbyte, 4 chiều với hai cổng 128 bit. Đây là bộ nhớ đệm ghi qua hỗ trợ lên đến hai lần tải 128 Byte mỗi chu kỳ. Nó được chia thành 16 ngân hàng, mỗi ngân hàng rộng 16 byte. [...] Chỉ có thể thực hiện một lần tải từ một ngân hàng nhất định của bộ đệm L1 trong một chu kỳ duy nhất.
Tóm lại:
Dòng bộ đệm 64 byte => 6 bit bù trong dòng bộ đệm
16KB / 4 chiều => cộng hưởng là 4KB.
Tức là các bit địa chỉ 0-5 là độ lệch dòng bộ nhớ cache.
16KB / 64B dòng bộ đệm => 2 ^ 14/2 ^ 6 = 2 ^ 8 = 256 dòng bộ đệm trong bộ đệm.
(Sửa lỗi: Ban đầu tôi đã tính sai giá trị này là 128. rằng tôi đã sửa tất cả các phần phụ thuộc.)
4 cách kết hợp => 256/4 = 64 chỉ mục trong mảng bộ nhớ cache. Tôi (Intel) gọi đây là "bộ".
tức là bạn có thể coi bộ đệm là một mảng gồm 32 mục nhập hoặc tập hợp, mỗi mục nhập chứa 4 dòng bộ nhớ cache quảng cáo thẻ của chúng. (Nó phức tạp hơn thế này, nhưng không sao).
(Nhân tiện, các thuật ngữ "set" và "way" có các định nghĩa khác nhau .)
có 6 bit chỉ số, bit 6-11 trong sơ đồ đơn giản nhất.
Điều này có nghĩa là bất kỳ dòng bộ đệm nào có cùng giá trị trong các bit chỉ mục, bit 6-11, sẽ ánh xạ đến cùng một bộ bộ đệm.
Bây giờ hãy nhìn vào chương trình của bạn.
C[dimension*i+j] += A[dimension*i+k] * B[dimension*k+j];
Vòng lặp k là vòng lặp trong cùng. Loại cơ sở là gấp đôi, 8 byte. Nếu thứ nguyên = 2048, tức là 2K, thì các phần tử liên tiếp của B[dimension*k+j]
vòng lặp được truy cập sẽ cách nhau 2048 * 8 = 16K byte. Tất cả chúng sẽ ánh xạ đến cùng một tập hợp của bộ đệm L1 - tất cả chúng sẽ có cùng một chỉ mục trong bộ đệm. Điều đó có nghĩa là, thay vì có 256 dòng bộ nhớ cache trong bộ nhớ cache có sẵn để sử dụng thì sẽ chỉ có 4 - "tính liên kết 4 chiều" của bộ nhớ cache.
Tức là bạn có thể sẽ bị lỗi bộ nhớ cache cứ sau 4 lần lặp lại xung quanh vòng lặp này. Không tốt.
(Thực ra, mọi thứ phức tạp hơn một chút. Nhưng trên đây là cách hiểu sơ bộ. Địa chỉ của các mục nhập của B được đề cập ở trên là địa chỉ ảo. Vì vậy, có thể có các địa chỉ vật lý hơi khác nhau. Hơn nữa, Bulldozer có cách dự đoán bộ nhớ cache, có thể sử dụng các bit địa chỉ ảo để không phải đợi bản dịch địa chỉ ảo sang địa chỉ vật lý. Tuy nhiên, trong mọi trường hợp: mã của bạn có "cộng hưởng" là 16K. Bộ đệm dữ liệu L1 có cộng hưởng là 16K. Không tốt .)]
Nếu bạn chỉ thay đổi thứ nguyên một chút, ví dụ thành 2048 + 1, thì địa chỉ của mảng B sẽ được trải rộng trên tất cả các bộ của bộ đệm. Và bạn sẽ nhận được ít bộ nhớ cache hơn đáng kể.
Đó là một cách tối ưu hóa khá phổ biến để đệm các mảng của bạn, ví dụ: thay đổi 2048 thành 2049, để tránh hiện tượng cộng hưởng này. Nhưng "chặn bộ nhớ cache là một cách tối ưu hóa thậm chí còn quan trọng hơn. Http://suif.stanford.edu/papers/lam-asplos91.pdf
Ngoài sự cộng hưởng của dòng bộ nhớ cache, có những thứ khác đang diễn ra ở đây. Ví dụ, bộ đệm L1 có 16 ngân hàng, mỗi dải rộng 16 byte. Với thứ nguyên = 2048, các lần truy cập B liên tiếp trong vòng lặp bên trong sẽ luôn đi đến cùng một ngân hàng. Vì vậy, chúng không thể đi song song - và nếu quyền truy cập A xảy ra đến cùng một ngân hàng, bạn sẽ thua.
Tôi không nghĩ, nhìn vào nó, điều này lớn như sự cộng hưởng từ bộ nhớ cache.
Và, vâng, có thể, có thể có răng cưa. Ví dụ: STLF (Bộ đệm lưu trữ để tải chuyển tiếp) có thể chỉ được so sánh bằng cách sử dụng một trường bit nhỏ và nhận được kết quả phù hợp sai.
(Thực ra, nếu bạn nghĩ về nó, cộng hưởng trong bộ nhớ cache giống như răng cưa, liên quan đến việc sử dụng các trường bit. Cộng hưởng là do nhiều dòng bộ nhớ cache ánh xạ cùng một tập hợp, không được lan truyền trước. Hiện tượng cộng hưởng là do đối sánh dựa trên địa chỉ không đầy đủ chút ít.)
Nhìn chung, đề xuất của tôi để điều chỉnh:
Thử chặn bộ nhớ cache mà không cần phân tích thêm. Tôi nói điều này vì chặn bộ nhớ cache rất dễ dàng và rất có thể đây là tất cả những gì bạn cần làm.
Sau đó, sử dụng VTune hoặc OProf. Hoặc Cachegrind. Hoặc là ...
Tốt hơn, hãy sử dụng một quy trình thư viện được điều chỉnh tốt để nhân ma trận.
O(n^3)
.