Làm thế nào để dòng cache hoạt động?


167

Tôi hiểu rằng bộ xử lý đưa dữ liệu vào bộ đệm thông qua các dòng bộ đệm, ví dụ, trên bộ xử lý Atom của tôi - mang lại khoảng 64 byte mỗi lần, bất kể kích thước của dữ liệu thực tế đang được đọc.

Câu hỏi của tôi là:

Hãy tưởng tượng rằng bạn cần đọc một byte từ bộ nhớ, 64 byte nào sẽ được đưa vào bộ đệm?

Hai khả năng tôi có thể thấy là, 64 byte bắt đầu ở ranh giới 64 byte gần nhất bên dưới byte quan tâm hoặc 64 byte được trải xung quanh byte theo một cách nào đó được xác định trước (ví dụ, một nửa dưới, một nửa ở trên, hoặc tất cả ở trên).

Đó là cái gì


22
Đọc này: Điều mà mọi lập trình viên nên biết về bộ nhớ . Sau đó đọc lại. Tốt hơn (pdf) nguồn ở đây .
andersoj

Câu trả lời:


128

Nếu dòng bộ đệm chứa byte hoặc từ bạn đang tải chưa có trong bộ đệm, CPU của bạn sẽ yêu cầu 64 byte bắt đầu ở ranh giới dòng bộ đệm (địa chỉ lớn nhất bên dưới địa chỉ bạn cần là bội số của 64) .

Các mô-đun bộ nhớ PC hiện đại truyền 64 bit (8 byte) cùng một lúc, trong tám lần chuyển , do đó, một lệnh sẽ kích hoạt đọc hoặc ghi một dòng bộ đệm đầy đủ từ bộ nhớ. (Kích thước truyền phát SDRAM DDR1 / 2/3/4 có thể định cấu hình lên đến 64B; CPU sẽ chọn kích thước truyền phát phù hợp với kích thước dòng bộ đệm của chúng, nhưng phổ biến là 64B)

Theo nguyên tắc thông thường, nếu bộ xử lý không thể dự đoán truy cập bộ nhớ (và tìm nạp trước), quá trình truy xuất có thể mất ~ 90 nano giây hoặc ~ 250 chu kỳ xung nhịp (từ CPU biết địa chỉ đến CPU nhận dữ liệu).

Ngược lại, một lần truy cập trong bộ đệm L1 có độ trễ sử dụng tải là 3 hoặc 4 chu kỳ và tải lại cửa hàng có độ trễ chuyển tiếp lưu trữ là 4 hoặc 5 chu kỳ trên CPU x86 hiện đại. Những điều tương tự trên các kiến ​​trúc khác.

Đọc thêm: Ulrich Drepper's Điều mà mọi lập trình viên nên biết về bộ nhớ . Lời khuyên về tìm nạp trước phần mềm đã hơi lỗi thời: các trình tìm nạp trước CT hiện đại thông minh hơn và siêu phân luồng tốt hơn so với trong P4 ngày (do đó, một luồng tìm nạp thường là lãng phí). Ngoài ra, wiki wiki có rất nhiều liên kết hiệu suất cho kiến ​​trúc đó.


1
Câu trả lời này hoàn toàn không có ý nghĩa. Băng thông bộ nhớ 64 bit (cũng không đúng về vấn đề đó) phải làm gì với bit 64 byte (!) Để làm gì? Ngoài ra, 10 đến 30 ns cũng hoàn toàn sai nếu bạn nhấn Ram. Nó có thể đúng với bộ đệm L3 hoặc L2 nhưng không đúng với RAM nơi nó giống như 90ns. Ý bạn là thời gian bùng nổ - thời gian để truy cập từ bốn từ tiếp theo trong chế độ chụp liên tục (đây thực sự là câu trả lời chính xác)
Martin Kersten

5
@MartinKersten: Một kênh của DDR1 / 2/3/4 SDRAM không sử dụng chiều rộng bus dữ liệu 64 bit. Một lần truyền toàn bộ dòng bộ đệm sẽ thực hiện tám lần chuyển 8B mỗi lần và đó là những gì thực sự xảy ra. Vẫn có thể đúng rằng quy trình được tối ưu hóa bằng cách chuyển đoạn dữ liệu được căn chỉnh 8B có chứa byte mong muốn trước, tức là bắt đầu cụm ở đó (và bao quanh nếu đó không phải là 8B đầu tiên của kích thước truyền nổ). Mặc dù vậy, các CPU hiện đại với bộ nhớ cache đa cấp có thể không còn làm điều đó nữa bởi vì điều đó có nghĩa là chuyển tiếp (các) khối đầu tiên của bộ đệm lên bộ đệm L1 sớm.
Peter Cordes

2
Haswell có đường dẫn 64B giữa bộ đệm L2 và L1D (nghĩa là độ rộng dòng bộ đệm đầy đủ), do đó, việc chuyển 8B chứa byte được yêu cầu sẽ khiến việc sử dụng bus đó không hiệu quả. @Martin cũng đúng về thời gian truy cập cho một tải phải vào bộ nhớ chính.
Peter Cordes

3
Câu hỏi hay về việc liệu dữ liệu có đi hết hệ thống phân cấp bộ nhớ cùng một lúc hay không, liệu L3 có đợi một dòng đầy đủ từ bộ nhớ hay không trước khi bắt đầu gửi nó lên L2. Có bộ đệm chuyển giữa các cấp bộ đệm khác nhau và mỗi lỗi bỏ sót yêu cầu một. Vì vậy ( tổng số phỏng đoán ) có lẽ L3 đặt các byte từ bộ điều khiển bộ nhớ vào bộ đệm nhận riêng của nó cùng lúc với việc đưa chúng vào bộ đệm tải thích hợp cho bộ đệm L2 muốn nó. Khi dòng được truyền hoàn toàn từ bộ nhớ, L3 thông báo cho L2 rằng dòng đã sẵn sàng và sao chép nó vào mảng của chính nó.
Peter Cordes

2
@Martin: Tôi quyết định đi trước và chỉnh sửa câu trả lời này. Tôi nghĩ bây giờ nó chính xác hơn, và vẫn đơn giản. Độc giả tương lai: xem thêm câu hỏi của Mike76 và câu trả lời của tôi: stackoverflow.com/questions/39182060/NH
Peter Cordes

22

Nếu các dòng bộ đệm rộng 64 byte, thì chúng tương ứng với các khối bộ nhớ bắt đầu trên các địa chỉ chia hết cho 64. 6 bit đáng kể nhất của bất kỳ địa chỉ nào được bù vào dòng bộ đệm.

Vì vậy, đối với bất kỳ byte đã cho nào, dòng bộ đệm phải tìm nạp có thể được tìm thấy bằng cách xóa sáu bit nhỏ nhất của địa chỉ, tương ứng với làm tròn xuống địa chỉ gần nhất chia hết cho 64.

Mặc dù điều này được thực hiện bằng phần cứng, chúng tôi có thể hiển thị các tính toán bằng một số định nghĩa macro C tham chiếu:

#define CACHE_BLOCK_BITS 6
#define CACHE_BLOCK_SIZE (1U << CACHE_BLOCK_BITS)  /* 64 */
#define CACHE_BLOCK_MASK (CACHE_BLOCK_SIZE - 1)    /* 63, 0x3F */

/* Which byte offset in its cache block does this address reference? */
#define CACHE_BLOCK_OFFSET(ADDR) ((ADDR) & CACHE_BLOCK_MASK)

/* Address of 64 byte block brought into the cache when ADDR accessed */
#define CACHE_BLOCK_ALIGNED_ADDR(ADDR) ((ADDR) & ~CACHE_BLOCK_MASK)

1
Tôi có thời gian khó khăn để hiểu điều này. Tôi biết là 2 năm sau, nhưng bạn có thể cho tôi mã ví dụ cho việc này không? một hoặc hai dòng.
Nick

1
@Nick Lý do phương pháp này hoạt động nằm trong hệ thống số nhị phân. Bất kỳ lũy thừa 2 nào cũng chỉ có một bit và tất cả các bit còn lại bị xóa, vì vậy, đối với 64, bạn 0b1000000, lưu ý rằng 6 chữ số cuối cùng là số không, vì vậy ngay cả khi bạn có một số với bất kỳ 6 số nào (đại diện cho số % 64), xóa chúng sẽ cung cấp cho bạn địa chỉ bộ nhớ được căn chỉnh 64 byte gần nhất.
huyền thoại2k

21

Trước hết một truy cập bộ nhớ chính là rất tốn kém. Hiện tại CPU 2GHz (chậm nhất một lần) có 2G tick (chu kỳ) mỗi giây. Một CPU (lõi ảo ngày nay) có thể lấy giá trị từ các thanh ghi của nó một lần mỗi lần đánh dấu. Do lõi ảo bao gồm nhiều đơn vị xử lý (ALU - đơn vị logic số học, FPU, v.v.) nên nó thực sự có thể xử lý các hướng dẫn nhất định song song nếu có thể.

Truy cập bộ nhớ chính có giá khoảng 70ns đến 100ns (DDR4 nhanh hơn một chút). Lần này về cơ bản là tìm kiếm bộ đệm L1, L2 và L3 và hơn là nhấn bộ nhớ (gửi lệnh đến bộ điều khiển bộ nhớ, gửi nó đến các ngân hàng bộ nhớ), chờ phản hồi và thực hiện.

100ns có nghĩa là khoảng 200 tick. Vì vậy, về cơ bản nếu một chương trình luôn bỏ lỡ bộ nhớ cache mà mỗi bộ nhớ truy cập, CPU sẽ dành khoảng 99,5% thời gian của nó (nếu nó chỉ đọc bộ nhớ) chờ trong bộ nhớ.

Để tăng tốc mọi thứ, có bộ đệm L1, L2, L3. Họ sử dụng bộ nhớ được đặt trực tiếp trên chip và sử dụng một loại mạch bán dẫn khác để lưu trữ các bit đã cho. Điều này chiếm nhiều dung lượng hơn, tốn nhiều năng lượng hơn và tốn kém hơn bộ nhớ chính do CPU thường được sản xuất bằng công nghệ tiên tiến hơn và lỗi sản xuất trong bộ nhớ L1, L2, L3 có thể khiến CPU không có giá trị (khiếm khuyết) bộ nhớ cache L1, L2, L3 lớn làm tăng tỷ lệ lỗi làm giảm năng suất làm giảm trực tiếp ROI. Vì vậy, có một sự đánh đổi lớn khi nói đến kích thước bộ đệm có sẵn.

(hiện tại người ta tạo thêm bộ đệm L1, L2, L3 để có thể hủy kích hoạt một số phần nhất định để giảm khả năng lỗi sản xuất thực tế là các vùng bộ nhớ cache biểu hiện toàn bộ lỗi CPU).

Để đưa ra ý tưởng về thời gian (nguồn: chi phí để truy cập bộ nhớ cache và bộ nhớ )

  • Bộ đệm L1: 1ns đến 2ns (2-4 chu kỳ)
  • Bộ đệm L2: 3ns đến 5ns (6-10 chu kỳ)
  • Bộ đệm L3: 12ns đến 20ns (24-40 chu kỳ)
  • RAM: 60ns (120 chu kỳ)

Vì chúng tôi trộn các loại CPU khác nhau, đây chỉ là ước tính nhưng sẽ có ý tưởng tốt về những gì sẽ xảy ra khi giá trị bộ nhớ được tải và chúng tôi có thể bị lỗi hoặc bỏ lỡ trong lớp bộ đệm nhất định.

Vì vậy, bộ nhớ cache về cơ bản tăng tốc truy cập bộ nhớ rất nhiều (60ns so với 1ns).

Tìm nạp một giá trị, lưu trữ nó trong bộ đệm để có cơ hội đọc lại, điều đó tốt cho các biến thường được truy cập nhưng đối với các hoạt động sao chép bộ nhớ thì sẽ vẫn chậm vì người ta chỉ đọc giá trị, ghi giá trị ở đâu đó và không bao giờ đọc giá trị một lần nữa ... không có lần truy cập bộ đệm, chết chậm (bên cạnh điều này có thể xảy ra song song vì chúng tôi đã thực hiện lệnh).

Bản sao bộ nhớ này rất quan trọng đến nỗi có nhiều phương tiện khác nhau để tăng tốc nó. Trong những ngày đầu, bộ nhớ thường có thể sao chép bộ nhớ ngoài CPU. Nó được xử lý trực tiếp bởi bộ điều khiển bộ nhớ, do đó thao tác sao chép bộ nhớ không gây ô nhiễm bộ đệm.

Nhưng bên cạnh một bộ nhớ đơn giản, việc truy cập bộ nhớ nối tiếp khác khá phổ biến. Một ví dụ là phân tích một loạt thông tin. Có một mảng các số nguyên và tính tổng, trung bình, trung bình hoặc thậm chí đơn giản hơn, tìm một giá trị nhất định (bộ lọc / tìm kiếm) là một loại thuật toán rất quan trọng khác chạy mỗi lần trên bất kỳ CPU mục đích chung nào.

Vì vậy, bằng cách phân tích mẫu truy cập bộ nhớ, rõ ràng dữ liệu được đọc tuần tự rất thường xuyên. Có khả năng cao là nếu một chương trình đọc giá trị tại chỉ mục i, thì chương trình đó cũng sẽ đọc giá trị i + 1. Xác suất này cao hơn một chút so với xác suất mà cùng một chương trình cũng sẽ đọc giá trị i + 2, v.v.

Vì vậy, được cung cấp một địa chỉ bộ nhớ, đó là (và vẫn là) một ý tưởng tốt để đọc trước và lấy các giá trị bổ sung. Đây là lý do tại sao có một chế độ tăng.

Truy cập bộ nhớ trong chế độ tăng có nghĩa là một địa chỉ được gửi và nhiều giá trị được gửi tuần tự. Mỗi giá trị gửi thêm chỉ mất khoảng 10ns bổ sung (hoặc thậm chí bên dưới).

Một vấn đề khác là một địa chỉ. Gửi một địa chỉ mất thời gian. Để giải quyết một phần lớn bộ nhớ, các địa chỉ lớn phải được gửi. Trong những ngày đầu, điều đó có nghĩa là bus địa chỉ không đủ lớn để gửi địa chỉ trong một chu kỳ (đánh dấu) và cần nhiều hơn một chu kỳ để gửi địa chỉ thêm chậm trễ.

Ví dụ, một dòng bộ đệm 64 byte có nghĩa là bộ nhớ được chia thành các khối bộ nhớ riêng biệt (không chồng lấp) có kích thước 64byte. 64byte có nghĩa là địa chỉ bắt đầu của mỗi khối có sáu bit địa chỉ thấp nhất luôn luôn là số không. Vì vậy, việc gửi sáu bit 0 này mỗi lần là không cần thiết, tăng không gian địa chỉ 64 lần cho bất kỳ số lượng chiều rộng bus địa chỉ nào (hiệu ứng chào mừng).

Một vấn đề khác mà dòng bộ đệm giải quyết (bên cạnh việc đọc trước và lưu / giải phóng sáu bit trên bus địa chỉ) nằm ở cách tổ chức bộ đệm. Ví dụ: nếu một bộ đệm sẽ được chia thành các khối 8 byte (64 bit) (các ô) thì người ta cần lưu trữ địa chỉ của ô nhớ mà ô bộ đệm này giữ giá trị cùng với nó. Nếu địa chỉ cũng là 64 bit, điều này có nghĩa là một nửa kích thước bộ đệm được sử dụng bởi địa chỉ dẫn đến chi phí là 100%.

Vì một dòng bộ đệm là 64byte và CPU có thể sử dụng 64bit - 6bit = 58bit (không cần lưu trữ các bit 0 quá đúng) có nghĩa là chúng ta có thể lưu trữ 64byte hoặc 512 bit với chi phí là 58 bit (11% trên không). Trong thực tế, các địa chỉ được lưu trữ thậm chí còn nhỏ hơn thế này nhưng có thông tin trạng thái (như dòng bộ đệm hợp lệ và chính xác, bẩn và cần phải ghi lại trong ram, v.v.).

Một khía cạnh khác là chúng ta có bộ đệm kết hợp thiết lập. Không phải mọi tế bào bộ đệm đều có thể lưu trữ một địa chỉ nhất định mà chỉ là một tập hợp con của những địa chỉ đó. Điều này làm cho các bit địa chỉ được lưu trữ cần thiết thậm chí còn nhỏ hơn, cho phép truy cập song song bộ đệm (mỗi tập hợp con có thể được truy cập một lần nhưng độc lập với các tập hợp con khác).

Điều đặc biệt hơn nữa là khi đồng bộ hóa truy cập bộ nhớ cache / bộ nhớ giữa các lõi ảo khác nhau, nhiều đơn vị xử lý độc lập của chúng trên mỗi lõi và cuối cùng là nhiều bộ xử lý trên một bo mạch chính (có các bo mạch chứa tới 48 bộ xử lý trở lên).

Về cơ bản, đây là ý tưởng hiện tại tại sao chúng ta có các dòng bộ đệm. Lợi ích của việc đọc trước là rất cao và trường hợp xấu nhất là đọc một byte đơn từ dòng bộ đệm và không bao giờ đọc phần còn lại là rất mong manh vì xác suất rất mỏng.

Kích thước của dòng bộ đệm (64) là sự đánh đổi được lựa chọn khôn ngoan giữa các dòng bộ đệm lớn hơn khiến cho byte cuối cùng của nó không thể đọc được trong tương lai gần, thời lượng cần thiết để tìm nạp dòng bộ đệm hoàn chỉnh từ bộ nhớ (và để ghi lại) và cả chi phí trong tổ chức bộ đệm và sự song song của bộ nhớ cache và truy cập bộ nhớ.


1
Bộ đệm liên kết bộ sử dụng một số bit địa chỉ để chọn một bộ, do đó các thẻ có thể thậm chí ngắn hơn ví dụ của bạn. Tất nhiên, bộ đệm cũng cần theo dõi thẻ nào đi với mảng dữ liệu nào trong tập hợp, nhưng thường có nhiều bộ hơn các cách trong một tập hợp. (ví dụ: Bộ nhớ cache L1D liên kết 8 chiều 32kB, với các dòng 64B, trong CPU Intel x86: offset 6 bit, chỉ số 6 bit. Các thẻ chỉ cần rộng 48-12 bit, vì hiện tại x86-64 (hiện tại) chỉ có 48- địa chỉ vật lý bit. Như tôi chắc chắn bạn biết, không phải ngẫu nhiên mà 12 bit thấp là phần bù trang, vì vậy L1 có thể là VIPT mà không cần răng cưa.)
Peter Cordes

nụ trả lời tuyệt vời ... có nút "thích" ở bất cứ đâu không?
Edgard Lima

@EdgardLima, không phải nút upvote?
Pacerier

6

Bộ xử lý có thể có bộ nhớ cache đa cấp (L1, L2, L3) và chúng khác nhau về kích thước và tốc độ.

Tuy nhiên, để hiểu chính xác những gì đi vào từng bộ đệm, bạn sẽ phải nghiên cứu bộ dự báo nhánh được sử dụng bởi bộ xử lý cụ thể đó và cách các hướng dẫn / dữ liệu của chương trình của bạn hoạt động theo nó.

Đọc về dự đoán nhánh , bộ đệm CPUcác chính sách thay thế .

Đây không phải là một nhiệm vụ dễ dàng. Nếu vào cuối ngày, tất cả những gì bạn muốn là một bài kiểm tra hiệu suất, bạn có thể sử dụng một công cụ như Cachegrind . Tuy nhiên, vì đây là một mô phỏng, kết quả của nó có thể khác nhau ở một mức độ nào đó.


4

Tôi không thể nói chắc chắn vì mọi phần cứng đều khác nhau, nhưng thông thường là "64 byte bắt đầu ở ranh giới 64 byte gần nhất bên dưới" vì đó là thao tác rất nhanh và đơn giản cho CPU.


2
Tôi có thể nói chắc chắn. Bất kỳ thiết kế bộ đệm hợp lý nào cũng sẽ có các dòng có kích thước bằng 2 và được căn chỉnh tự nhiên. (ví dụ: liên kết 64B). Nó không chỉ nhanh và đơn giản, mà theo nghĩa đen là miễn phí: bạn chỉ cần bỏ qua 6 bit thấp của địa chỉ. Bộ nhớ cache thường làm những việc khác nhau với phạm vi địa chỉ khác nhau. (ví dụ như bộ nhớ cache quan tâm về thẻ và chỉ số để phát hiện hit vs miss, sau đó chỉ bằng cách sử dụng bù đắp trong một dòng bộ nhớ cache cho chèn / giải nén dữ liệu)
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.