2009-12-21 36 views
7

Tôi đã đọc Ulrich Drepper, "What every programmer should know about memory" và trong phần 3.3.2 Measurements of Cache Effects (nửa chừng dưới trang) nó cho tôi ấn tượng rằng truy cập bất kỳ thành viên nào của cấu trúc làm cho toàn bộ cấu trúc bị kéo vào bộ nhớ cache CPU.Việc truy cập một thành viên cấu trúc đơn lẻ có kéo toàn bộ cấu trúc vào Cache không?

Điều này có đúng không? Nếu vậy, làm thế nào để phần cứng biết về cách bố trí của các cấu trúc này? Hoặc không mã được tạo ra bởi trình biên dịch bằng cách nào đó lực lượng toàn bộ cấu trúc được nạp?

Hoặc là sự chậm lại từ việc sử dụng cấu trúc lớn hơn chủ yếu do TLB bỏ lỡ do các cấu trúc được trải ra trên nhiều trang bộ nhớ hơn?

Ví dụ struct được sử dụng bởi Drepper là:

struct l { 
    struct l *n; 
    long int pad[NPAD]; 
    }; 

đâu sizeof(l) được xác định bởi NPAD bằng 0, 7, 15 hoặc 31 kết quả trong cấu trúc đó là 0, 56, 120, và 248 byte ngoài và giả định các dòng bộ nhớ cache là 64 byte và 4k trang.

Chỉ cần lặp qua danh sách được liên kết sẽ chậm hơn đáng kể khi cấu trúc phát triển, mặc dù không có gì khác ngoài con trỏ thực sự được truy cập.

Trả lời

8

Phần cứng không biết gì về cấu trúc. Nhưng đúng là phần cứng tải trong bộ nhớ cache một số byte xung quanh các byte bạn đang thực sự truy cập. Điều này là do dòng bộ nhớ cache có kích thước. Nó không hoạt động trên byte bằng truy cập byte nhưng trên ví dụ: Kích thước 16 byte tại một thời điểm.

Bạn phải cẩn thận khi đặt hàng các thành viên của cấu trúc sao cho các thành viên thường được sử dụng ở gần nhau. Ví dụ: nếu bạn có cấu trúc sau:

struct S { 
    int foo; 
    char name[64]; 
    int bar; 
}; 

Nếu biến thành viên và thanh được sử dụng rất thường xuyên, phần cứng sẽ tải vào bộ nhớ cache byte xung quanh foo và khi bạn truy cập vào thanh, nó sẽ có để tải các byte xung quanh thanh. Ngay cả khi các byte xung quanh foo và thanh xung quanh không bao giờ được sử dụng. Bây giờ, hãy viết lại cấu trúc của bạn như sau:

struct S { 
    int foo; 
    int bar; 
    char name[64]; 
}; 

Khi bạn sử dụng foo, phần cứng sẽ tải bộ nhớ cache các byte xung quanh foo. Khi bạn sẽ sử dụng thanh, thanh sẽ có trong bộ nhớ cache vì thanh được chứa trong các byte xung quanh foo. CPU sẽ không phải đợi thanh nằm trong bộ đệm.

trả lời là: truy cập vào một thành viên struct duy nhất không kéo toàn bộ cấu trúc trong bộ nhớ cache, nhưng kéo một số thành viên khác của struct vào bộ nhớ cache.

8

Phần cứng không biết bố cục của cấu trúc, nhưng chỉ tải một số byte xung quanh thành viên được truy cập vào bộ nhớ cache. Và có, sự chậm lại từ các cấu trúc lớn hơn là vì chúng sẽ được lan truyền qua nhiều dòng bộ nhớ cache hơn.

+0

Điều này là chính xác. Các khái niệm ưa thích ở đây là địa phương tham khảo. – jason

+0

Vì vậy, khi bạn nói "trải rộng trên nhiều dòng bộ nhớ cache", bạn có nghĩa là một phần của kết quả chậm từ việc tìm nạp trước các phần không sử dụng của cấu trúc xung quanh, ngoài TLB bỏ sót. –

+1

@Robert: Tôi nghĩ rằng nó có thể áp dụng theo hai cách khác nhau: 1. Một cấu trúc đơn lẻ quá lớn đến nỗi nó không vừa vặn trong một trang bộ đệm. Nếu bạn "chạm vào nó trên tất cả" (âm thanh bẩn) nó có thể sẽ gây ra nhiều lần tải trang. 2. Với cấu trúc lớn hơn, bạn chỉ nhận được ít cấu trúc hơn vào bộ nhớ cache với bất kỳ trang bộ nhớ cache nào đọc, do đó làm tăng khả năng rằng * cấu trúc * tiếp theo mà bạn muốn không có trong bộ nhớ cache. Một vấn đề cơ bản ở đây là * địa phương tham chiếu *. Nếu bạn hopscotch xung quanh bộ nhớ, bạn sẽ có nhiều bộ nhớ cache nhớ. Hiểu mẫu truy cập của bạn và thiết kế phù hợp. –

1

Thông thường, bộ nhớ cache L1 sử dụng virtual addresses, nếu bạn truy cập vào thành viên struct, một lượng byte cụ thể sẽ được lưu vào bộ nhớ cache (một cache line, kích thước thường từ 8 đến 512 byte). Vì tất cả các thành viên struct được căn chỉnh song song trong bộ nhớ, cơ hội mà toàn bộ cấu trúc được lưu trong bộ nhớ cache hơi lớn (phụ thuộc vào sizeof(struct your_struct)) ...

3

Truy cập thành viên cấu trúc không bị phạt hiệu suất nhiều hơn truy cập bất kỳ khu vực nào khác trong bộ nhớ. Trong thực tế, có thể có một cải tiến hiệu suất nếu bạn truy cập vào một số thành viên cấu trúc trong cùng một khu vực, vì các thành viên khác có thể được lưu trữ trong lần truy cập đầu tiên.

1

Trong khi CPU có thể vui vẻ xử lý các tải và lưu trữ nhỏ như một byte, bộ đệm chỉ bao giờ xử lý dữ liệu có kích thước "bộ nhớ cache". Trong sách giáo khoa kiến ​​trúc máy tính, điều này còn được gọi là "kích thước khối".

Trên hầu hết các hệ thống, đây là 32 hoặc 64 byte. Nó có thể khác với một CPU đến CPU tiếp theo, và thậm chí đôi khi từ cấp độ cache này sang cấp độ cache khác.

Ngoài ra, một số CPU thực hiện tìm nạp trước; điều này có nghĩa là nếu bạn truy cập vào bộ nhớ cache 5 và 6 theo thứ tự, nó sẽ cố gắng tải bộ đệm ẩn 7 mà không cần bạn yêu cầu.

1

"Chỉ cần lặp qua danh sách được liên kết sẽ chậm hơn đáng kể khi cấu trúc phát triển, mặc dù không có gì khác ngoài con trỏ thực sự được truy cập".

Với NPAD = 0, mỗi dòng bộ nhớ cache chứa 8 nút danh sách, vì vậy bạn có thể thấy lý do tại sao điều đó nhanh nhất.

Với NPAD = 7, 15, 31, chỉ một dòng bộ nhớ cache cần được tải cho mỗi nút danh sách và bạn có thể mong đợi tất cả chúng đều có cùng tốc độ - một bộ nhớ cache bị thiếu trên mỗi nút. Nhưng một người quản lý bộ nhớ hiện đại sẽ làm bộ nhớ đệm đầu cơ. Nếu nó có dung lượng dự phòng (vì nó có thể làm, vì với bộ nhớ hiện đại, nó có thể thực hiện nhiều lần đọc song song với bộ nhớ chính), sau đó nó sẽ bắt đầu tải bộ nhớ gần bộ nhớ bạn đang sử dụng. Mặc dù đó là một danh sách liên kết, nếu bạn xây dựng nó theo bất kỳ cách nào rõ ràng thì có một cơ hội tốt để bạn truy cập bộ nhớ theo thứ tự. Vì vậy, gần nhau hơn trong bộ nhớ danh sách các nút của bạn, bộ nhớ cache thành công hơn có khả năng được trong điều khoản của đã có những gì bạn cần.

Trong trường hợp tồi tệ nhất có thể, khi bộ nhớ của bạn được kéo vào từ trao đổi khi bạn sử dụng nó, chương trình của bạn sẽ bị giới hạn bởi đĩa I/O. Có thể là tốc độ tiến bộ của bạn thông qua danh sách sẽ được xác định hoàn toàn bởi số lượng nút có trên mỗi trang và bạn có thể thấy thời gian được thực hiện tỷ lệ thuận với kích thước của nút, tối đa 4k. Tôi đã không thử nó, mặc dù, và hệ điều hành sẽ được thông minh với trao đổi cũng giống như MMU là thông minh với bộ nhớ chính, vì vậy nó không nhất thiết phải đơn giản.

Các vấn đề liên quan