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

在C++函数范围静态与可变成员变量中缓存昂贵数据

  •  9
  • 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 年前

    我将用类似的东西来包装longrunningoperationtofectfactor的实现。我使用的是boost-scoped锁,但您可以使用类似于其他锁定框架的锁。

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

    如果这真的是一个长时间运行的操作,那么锁定互斥体的成本应该是最小的。

    您的问题并不十分清楚,但是如果函数longrunningoperationtofectfactor是您类的成员函数,那么您希望该映射是同一类中的可变映射。我单一的静态互斥访问仍然足够快。

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

    这样,你只取一次因子。