2009-07-27 37 views
108

"Chỉ có hai vấn đề khó khăn trong Khoa học Máy tính: không hợp lệ bộ nhớ cache và đặt tên cho mọi thứ".Gỡ lỗi bộ nhớ cache - Có giải pháp chung không?

Phil Karlton

Có một giải pháp chung hoặc phương pháp để hủy bỏ hiệu lực một bộ nhớ cache; để biết khi nào mục nhập là cũ, vì vậy bạn được đảm bảo luôn nhận được dữ liệu mới? Ví dụ, xem xét một hàm getData() lấy dữ liệu từ một tệp. Nó lưu trữ nó dựa trên thời gian sửa đổi cuối cùng của tập tin, mà nó kiểm tra mỗi khi nó được gọi.
Sau đó, bạn thêm hàm thứ hai transformData() để chuyển đổi dữ liệu và lưu trữ kết quả của nó trong lần tiếp theo hàm được gọi. Nó không có kiến ​​thức về các tập tin - làm thế nào để bạn thêm sự phụ thuộc rằng nếu tập tin được thay đổi, bộ nhớ cache này trở thành không hợp lệ?

Bạn có thể gọi getData() mỗi lần transformData() được gọi và so sánh với giá trị được sử dụng để tạo bộ nhớ cache, nhưng điều đó có thể sẽ rất tốn kém.

+6

Tôi tin rằng anh ấy đang làm gì đó với việc viết X Windows – Greg

+1

Tôi nghĩ rằng tiêu đề sẽ tốt hơn là "Cache Invalidation - Có giải pháp chung không?" vì nó đề cập đến một lớp cụ thể của vấn đề bộ nhớ đệm. – RBarryYoung

+71

Không, anh ta không biết nhiều về khoa học máy tính. Tôi chắc chắn rằng sự tham gia của anh ấy trong việc tạo OpenGL, X11 và SSLv3 khiến anh ấy quá bận rộn để thực sự nghiên cứu nó nhiều. :-) –

Trả lời

51

Những gì bạn đang nói về là chuỗi sự phụ thuộc suốt đời, có một điều phụ thuộc vào cái khác có thể được sửa đổi ngoài tầm kiểm soát của nó.

Nếu bạn có một chức năng idempotent từ a, b-c ở đâu, nếu ab đều giống nhau sau đó c là như nhau nhưng chi phí kiểm tra b là cao, sau đó bạn có:

  1. chấp nhận rằng đôi khi bạn hoạt động với thông tin lỗi thời và không phải lúc nào cũng kiểm tra b
  2. làm tốt nhất cấp của bạn để kiểm tra b nhanh nhất có thể

Bạn không thể có bánh của bạn và ăn nó ...

Nếu bạn có thể lớp một bộ nhớ cache bổ sung dựa trên a trên đầu thì đây ảnh hưởng đến vấn đề ban đầu không một chút. Nếu bạn chọn 1 thì bạn có bất kỳ sự tự do nào mà bạn đã cho bản thân và do đó có thể nhớ nhiều hơn nhưng phải nhớ xem xét tính hợp lệ của giá trị được lưu trong bộ nhớ cache của b. Nếu bạn chọn 2, bạn vẫn phải kiểm tra b mỗi lần nhưng có thể quay lại bộ nhớ cache cho a nếu b kiểm tra.

Nếu bạn lưu trữ lớp, bạn phải xem xét liệu bạn có vi phạm 'quy tắc' của hệ thống do hành vi kết hợp hay không.

Nếu bạn biết rằng a luôn luôn có giá trị pháp lý nếu b không thì bạn có thể sắp xếp bộ nhớ cache của bạn như vậy (giả):

private map<b,map<a,c>> cache // 
private func realFunction // (a,b) -> c 

get(a, b) 
{ 
    c result; 
    map<a,c> endCache; 
    if (cache[b] expired or not present) 
    { 
     remove all b -> * entries in cache; 
     endCache = new map<a,c>();  
     add to cache b -> endCache; 
    } 
    else 
    { 
     endCache = cache[b];  
    } 
    if (endCache[a] not present)  // important line 
    { 
     result = realFunction(a,b); 
     endCache[a] = result; 
    } 
    else 
    { 
     result = endCache[a]; 
    } 
    return result; 
} 

lớp Rõ ràng liên tiếp (nói x) là tầm thường chừng nào, ở từng giai đoạn tính hợp lệ của đầu vào mới được thêm vào khớp với mối quan hệ a: b cho x: bx: a.

Tuy nhiên, bạn hoàn toàn có thể nhận được ba yếu tố đầu vào có giá trị hoàn toàn độc lập (hoặc là theo chu kỳ), vì vậy không thể phân lớp được. Điều này có nghĩa dòng đánh dấu quan trọng // sẽ phải thay đổi để

if (endCache [a] hết hạn hoặc không có mặt)

+3

hoặc có thể, nếu chi phí kiểm tra b cao, bạn sử dụng pubsub để khi b thay đổi, thông báo c. Mẫu Observer là phổ biến. – user1031420

-2

Có lẽ thuật toán không có bộ nhớ cache sẽ là tổng quát nhất (hoặc ít nhất, ít cấu hình phần cứng phụ thuộc hơn), vì chúng sẽ sử dụng bộ nhớ cache nhanh nhất trước và tiếp tục từ đó. Đây là một bài giảng của MIT về nó: Cache Oblivious Algorithms

+3

Tôi nghĩ rằng anh ấy không nói về bộ nhớ đệm phần cứng - anh ấy nói về getData của mình() mã có một tính năng "lưu trữ" dữ liệu mà anh nhận được từ một tệp vào bộ nhớ. – Alex319

2

Tôi đang làm việc trên một phương pháp ngay bây giờ dựa trên PostSharpmemoizing functions. Tôi đã chạy nó qua người cố vấn của tôi, và ông đồng ý rằng nó là một thực hiện tốt của bộ nhớ đệm theo một cách nội dung bất khả tri.

Mọi chức năng đều có thể được đánh dấu bằng thuộc tính chỉ định thời gian hết hạn. Mỗi chức năng được đánh dấu theo cách này được ghi nhớ và kết quả được lưu vào bộ đệm, với một hàm băm của cuộc gọi hàm và tham số được sử dụng làm khóa. Tôi đang sử dụng Velocity cho chương trình phụ trợ, xử lý phân phối dữ liệu bộ nhớ cache.

1

Có giải pháp chung hoặc phương pháp tạo bộ nhớ cache, để biết thời điểm mục nhập đã cũ, vì vậy bạn được đảm bảo luôn nhận được dữ liệu mới?

Không, vì tất cả dữ liệu đều khác nhau. Một số dữ liệu có thể là "cũ" sau một phút, một số sau một giờ và một số có thể tốt cho ngày hoặc tháng.

Về ví dụ cụ thể của bạn, giải pháp đơn giản nhất là có chức năng 'kiểm tra bộ nhớ cache' cho các tệp mà bạn gọi từ cả hai số getDatatransformData.

3

Nếu bạn định chuyển sang getData() mỗi khi bạn thực hiện phép biến đổi, thì bạn đã loại bỏ toàn bộ lợi ích của bộ nhớ cache. Ví dụ của bạn, nó có vẻ như một giải pháp sẽ là khi bạn tạo dữ liệu được chuyển đổi, để lưu trữ tên tệp và thời gian sửa đổi cuối cùng của tệp dữ liệu được tạo ra (bạn đã lưu trữ dữ liệu này trong cấu trúc dữ liệu bất kỳ là gì trả về bởi getData(), vì vậy bạn chỉ cần sao chép bản ghi đó vào cấu trúc dữ liệu được trả về bởi transformData()) và sau đó khi bạn gọi hàm transformData() một lần nữa, hãy kiểm tra thời gian sửa đổi cuối cùng của tệp.

14

Các vấn đề trong huỷ bỏ hiệu lực bộ nhớ cache là thay đổi những thứ mà không cần chúng tôi biết về nó. Vì vậy, trong một số trường hợp, một giải pháp là có thể nếu có một số điều khác mà không biết về nó và có thể thông báo cho chúng tôi. Trong ví dụ đã cho, hàm getData có thể móc vào hệ thống tệp, không biết về tất cả các thay đổi đối với tệp, bất kể quy trình nào thay đổi tệp và thành phần này lần lượt có thể thông báo cho thành phần biến đổi dữ liệu.

Tôi không nghĩ rằng có bất kỳ sửa chữa ma thuật chung nào để làm cho vấn đề biến mất. Nhưng trong nhiều trường hợp thực tế, có thể rất có cơ hội để chuyển đổi một phương pháp tiếp cận dựa trên "bỏ phiếu" thành một bước "gián đoạn", điều này có thể làm cho vấn đề đơn giản biến mất.

1

Không có giải pháp chung nhưng:

  • Bạn nhớ cache có thể hoạt động như một proxy (kéo).Giả sử bộ nhớ cache của bạn biết dấu thời gian của biến đổi ban đầu, khi ai đó gọi getData(), bộ nhớ cache yêu cầu xuất xứ cho dấu thời gian thay đổi cuối cùng của nó, nếu như vậy, nó trả về bộ nhớ cache, nếu không nó sẽ cập nhật nội dung của nó bằng nguồn và trả lại nội dung của nó. (Biến thể là khách hàng gửi trực tiếp dấu thời gian theo yêu cầu, nguồn sẽ chỉ trả lại nội dung nếu dấu thời gian của nó khác nhau.)

  • Bạn vẫn có thể sử dụng quy trình thông báo (push), bộ nhớ cache quan sát nguồn, nếu nguồn thay đổi, nó sẽ gửi một thông báo đến bộ nhớ cache mà sau đó được gắn cờ là "bẩn". Nếu ai đó gọi số getData() bộ nhớ cache trước tiên sẽ được cập nhật lên nguồn, xóa cờ "bẩn"; sau đó trả lại nội dung của nó.

Sự lựa chọn nói chung phụ thuộc vào:

  • Tần số: nhiều cuộc gọi trên getData() muốn đẩy như vậy để tránh những nguồn được tràn ngập bởi một hàm getTimestamp
  • Truy cập của bạn đến nguồn: Bạn có đang sở hữu mô hình nguồn không? Nếu không, rất có thể là bạn không thể thêm bất kỳ quy trình thông báo nào.

Lưu ý: Khi sử dụng dấu thời gian là cách thức truyền thống http proxy đang hoạt động, cách tiếp cận khác là chia sẻ băm nội dung được lưu trữ. Cách duy nhất tôi biết cho 2 thực thể để được cập nhật cùng nhau là tôi gọi cho bạn (kéo) hoặc bạn gọi cho tôi ... (push) đó là tất cả.

3

IMHO, Lập trình phản hồi chức năng (FRP) là một cách tổng quát để giải quyết việc vô hiệu hóa bộ nhớ cache.

Đây là lý do: dữ liệu cũ trong thuật ngữ FRP được gọi là glitch. Một trong những mục tiêu của FRP là đảm bảo sự vắng mặt của các trục trặc.

FRP được giải thích chi tiết hơn trong số 'Essence of FRP' talk và trong số SO answer này.

Trong các talk các Cell s đại diện cho một đối tượng được lưu trong bộ nhớ cache/thực thể và Cell được làm mới nếu một trong những phụ thuộc của nó được làm mới.

FRP ẩn mã đường ống dẫn nước được liên kết với biểu đồ phụ thuộc và đảm bảo rằng không có cũ Cell s.

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