代码之家  ›  专栏  ›  技术社区  ›  Dominic Rodger

在C++中缓存昂贵的数据——函数作用域静态变量与可变成员变量

  •  10
  • Dominic Rodger  · 技术社区  · 16 年前

    我有一个相对昂贵的数据获取操作,我想缓存其结果。此操作从调用 const

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

    AdjustData mutable map<int, double> key factor ),但我认为使用函数范围的静态可能是一个更好的解决方案——这个因素只需要这个函数,与类的其他部分无关。

    Dom

    4 回复  |  直到 16 年前
        1
  •  5
  •   Jeroen Dirks    16 年前

    我会用这样的东西来包装LongRunningOperationToFetchFactor的实现。我使用的是Boost作用域锁,但你也可以使用其他锁框架。

    #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;
    }
    

    从你的问题中不太清楚,但如果函数LongRunningOperationToFetchFactor是你类的成员函数,那么你希望映射是同一类中的可变映射。不过,我使用单个静态互斥体进行访问仍然足够快。

        2
  •  3
  •   Johannes Schaub - litb    16 年前

    我会

    正如你所说,它应该是线程安全的——如果不同的线程可以调用同一对象上的成员函数,你可能想使用互斥体。 boost::thread 是一个很好的图书馆。

        3
  •  1
  •   Judge Maygarden    16 年前

    singleton pattern (1) 使用一个执行长时间运行操作并缓存结果的类。然后,这个实例可以在其他类的const成员函数中使用。考虑互斥来保护映射数据结构中的插入和提取,以确保线程安全。如果多线程性能是一个巨大的问题,那么您可以将键标记为正在进行中,以防止多个线程同时计算同一个键。

    #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) 单例模式可能会被严重遗漏。所以,如果你是第一次看到它,请不要疯狂。

        4
  •  0
  •   Lyndsey Ferguson    16 年前

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

    这样你只取一次因子。