2009-03-05 19 views
7

Tôi đã có một hoạt động tìm nạp dữ liệu tương đối đắt tiền mà tôi muốn lưu vào bộ nhớ cache kết quả. Thao tác này được gọi là từ const phương pháp, đại khái như thế này:Caching dữ liệu đắt tiền trong C++ - statics chức năng scoped vs biến thành viên có thể thay đổi

double AdjustData(double d, int key) const { 
    double factor = LongRunningOperationToFetchFactor(key); 
    return factor * d; 
} 

Tôi muốn AdjustData vẫn const, nhưng tôi muốn để cache ra các yếu tố vì vậy tôi chỉ lấy nó lần đầu tiên. Hiện tại, tôi đang sử dụng mutable map<int, double> để lưu trữ kết quả (bản đồ là từ key đến factor), nhưng tôi đang nghĩ rằng việc sử dụng chức năng tĩnh có thể là giải pháp tốt hơn - yếu tố này chỉ cần bởi hàm này và không liên quan đến phần còn lại của lớp.

Điều đó có vẻ như là một cách hay không? Có lựa chọn nào tốt hơn không? Những điều tôi có thể suy nghĩ về, đặc biệt là đối với thread-an toàn.

Cảm ơn,

Dom

Trả lời

5

Tôi sẽ bao bọc việc triển khai LongRunningOperationToFetchFactor với một cái gì đó như thế này. Tôi đang sử dụng Boost scoped khóa nhưng bạn có thể như vậy một cái gì đó tương tự với các khung khóa khác.

#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <map> 

using namespace std; 

static boost::mutex myMutex; 
static map<int,double> results; 

double CachedLongRunningOperationToFetchFactor(int key) 
{ 

    { 
     boost::mutex::scoped_lock lock(myMutex); 

     map<int,double>::iterator iter = results.find(key); 
     if (iter != results.end()) 
     { 
      return (*iter).second; 
     } 
    } 
    // not in the Cache calculate it 
    result = LongRunningOperationToFetchFactor(key); 
    { 
     // we need to lock the map again 
     boost::mutex::scoped_lock lock(myMutex); 
     // it could be that another thread already calculated the result but 
     // map assignment does not care. 
     results[key] = result; 
    } 
    return result; 
} 

Nếu điều này thực sự là một hoạt động dài thì chi phí khóa Mutex phải nhỏ nhất.

Nó không phải là khá rõ ràng từ bạn câu hỏi nhưng nếu các chức năng LongRunningOperationToFetchFactor là một chức năng thành viên của lớp bạn thì bạn muốn bản đồ là bản đồ có thể thay đổi trong cùng một lớp. Tôi duy nhất mutex tĩnh cho truy cập vẫn còn đủ nhanh mặc dù.

+0

Có thể không cần thiết phải khóa thao tác chạy dài, chỉ cần tìm/chèn các cuộc gọi trên bản đồ. –

+0

Tôi nghĩ bạn đúng. Hãy để tôi điều chỉnh nó hơn nữa. –

+0

Tạo bản đồ tĩnh trong khi không giữ khóa không được đảm bảo an toàn chỉ vì bạn có thể xây dựng hai lần và hủy đôi bản đồ nếu hai chủ đề gọi hàm này đồng thời lần đầu tiên được sử dụng. Xem http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx – bk1e

0

Trừ khi tôi không hiểu, có vẻ như rõ ràng với tôi rằng bạn muốn làm điều này một tĩnh:

double AdjustData(double d) const { 
    static const double kAdjustFactor = LongRunningOperationToFetchFactor(); 
    return kAdjustFactor * d; 
} 

Bằng cách đó bạn chỉ lấy yếu tố Một lần.

+0

Xin chào Lyndsey - cảm ơn, đó là những gì tôi đang nghĩ. Tôi đã chỉnh sửa câu hỏi của mình một chút để xác định vấn đề thực tế. Tôi không thể sử dụng một const tĩnh trong trường hợp này, vì vậy tôi đoán tôi sử dụng một bản đồ tĩnh và làm một số loại khóa xung quanh chèn bản đồ. Điều đó có đúng không? –

+0

Những gì bạn đang nói về sẽ làm việc, nhưng giải pháp có một chút * xấu * mùi :) (Lấy từ cuốn sách Refactoring). Đây có phải là thứ mà người dùng khởi tạo cho nhiều 'khóa' khác nhau mà bạn có thể chạy trên một chuỗi riêng biệt và hiển thị kết quả khi nó được thực hiện không? –

+0

Nếu bạn làm cho nó tĩnh, tất cả các đối tượng của lớp sẽ chia sẻ giá trị. Đây có phải là những gì bạn muốn? –

1

Bạn có thể sử dụng singleton pattern (1) với một lớp thực hiện hoạt động lâu dài và lưu trữ kết quả. Ví dụ này sau đó có thể được sử dụng trong các hàm thành viên const của các lớp khác. Xem xét loại trừ lẫn nhau để bảo vệ chèn và trích xuất từ ​​cấu trúc dữ liệu bản đồ cho an toàn luồng. Nếu hiệu suất đa luồng là một vấn đề lớn, thì bạn có thể gắn cờ các phím như đang được tiến hành để ngăn chặn nhiều luồng cùng lúc tính toán cùng một khóa.

#include <cstdlib> 
#include <iostream> 
#include <map> 

using namespace std; 

class FactorMaker { 
    map<int, double> cache; 

    double longRunningFetch(int key) 
    { 
     const double factor = static_cast<double> (rand())/RAND_MAX; 
     cout << "calculating factor for key " << key << endl; 
     // lock 
     cache.insert(make_pair(key, factor)); 
     // unlock 
     return factor; 
    } 

public: 
    double getFactor(int key) { 
     // lock 
     map<int, double>::iterator it = cache.find(key); 
     // unlock 
     return (cache.end() == it) ? longRunningFetch(key) : it->second; 
    } 
}; 

FactorMaker & getFactorMaker() 
{ 
    static FactorMaker instance; 
    return instance; 
} 

class UsesFactors { 
public: 
    UsesFactors() {} 

    void printFactor(int key) const 
    { 
     cout << getFactorMaker().getFactor(key) << endl; 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    const UsesFactors obj; 

    for (int i = 0; i < 10; ++i) 
     obj.printFactor(i); 

    for (int i = 0; i < 10; ++i) 
     obj.printFactor(i); 

    return EXIT_SUCCESS; 
} 

(1) Mẫu đơn có thể bị bỏ qua. Vì vậy, xin vui lòng không bị điên với nó nếu bạn đang nhìn thấy nó lần đầu tiên.

3

Tôi sẽ không làm cho bộ nhớ cache này là địa phương tĩnh. Bản đồ có thể thay đổi là giải pháp để lưu trữ kết quả. Nếu không, nó sẽ làm cho hàm của bạn vô dụng, vì các đối tượng khác nhau của lớp sẽ chia sẻ cùng một bộ nhớ cache, vì bộ nhớ cache tĩnh cục bộ giống nhau cho tất cả các đối tượng. Bạn có thể sử dụng static cục bộ nếu kết quả không phụ thuộc vào đối tượng. Nhưng sau đó tôi sẽ tự hỏi tại sao chức năng này là một thành viên không tĩnh của đối tượng của bạn, nếu nó không cần truy cập bất kỳ trạng thái nào của nó.

Như bạn nói nó nên được an toàn thread - nếu chủ đề khác nhau có thể gọi chức năng thành viên trên cùng một đối tượng, bạn có thể muốn sử dụng một mutex.boost::thread là một thư viện tốt để sử dụng.

+1

Vì vậy, chức năng là một thành viên không tĩnh của đối tượng vì nó trước đây đã phụ thuộc vào trạng thái của đối tượng (bản đồ có thể thay đổi). Với một tĩnh cục bộ, tôi có thể làm cho hàm này tĩnh. –

+0

alright đó là một điều khác nhau sau đó :) chỉ muốn cảnh báo bạn về điều này nếu LongRunningOperation là một chức năng thành viên phụ thuộc vào đối tượng của bạn :) người ta không thể cẩn thận đủ :) –

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