Katana Plug-in APIs 0.1

AttributeKeyedCache.h

00001 #ifndef INCLUDED_FNGEOLIBUTIL_ATTRIBUTEKEYEDCACHE_H
00002 #define INCLUDED_FNGEOLIBUTIL_ATTRIBUTEKEYEDCACHE_H
00003 
00004 #include "ns.h"
00005 
00006 #include <atomic>
00007 #include <cstdlib>
00008 #include <memory>
00009 #include <mutex>
00010 #include <string>
00011 #ifdef ATTRIBUTEKEYEDCACHE_USE_BOOST
00012 #include <boost/unordered_map.hpp>
00013 #include <boost/unordered_set.hpp>
00014 #else
00015 #include <map>
00016 #include <set>
00017 #endif
00018 #include <vector>
00019 
00020 #ifndef ATTRIBUTEKEYEDCACHE_NO_TBB
00021 #include <tbb/concurrent_hash_map.h>
00022 #include <tbb/concurrent_queue.h>
00023 #endif
00024 
00025 #include <FnAttribute/FnAttribute.h>
00026 
00027 FNGEOLIBUTIL_NAMESPACE_ENTER
00028 {
00029     template <class T, class PointerT = typename std::shared_ptr<T>>
00030     class AttributeKeyedCacheMutex
00031     {
00032     public:
00033         typedef PointerT IMPLPtr;
00034 
00035 #ifdef ATTRIBUTEKEYEDCACHE_USE_BOOST
00036         using KeyMapType = boost::unordered_map<uint64_t, IMPLPtr>;
00037         using KeySetType = boost::unorderd_set<uint64_t>;
00038 #else
00039         using KeyMapType = std::map<uint64_t, IMPLPtr>;
00040         using KeySetType = std::set<uint64_t>;
00041 #endif
00042 
00043         AttributeKeyedCacheMutex(
00044             const std::size_t maxNumEntries = 0xffffffff,
00045             const std::size_t maxNumInvalidKeys = 0xffffffff)
00046             : m_maxNumEntries(maxNumEntries),
00047               m_maxNumInvalidKeys(maxNumInvalidKeys)
00048         {
00049         }
00050 
00051         virtual ~AttributeKeyedCacheMutex() {}
00052 
00053         IMPLPtr getValue(const FnAttribute::Attribute& iAttr)
00054         {
00055             // For these purposes, good enough to use a simple 64-bit hash.
00056             // The rationale is that AttributeKeyedCache should not grow to
00057             // the point where 2**64th hash entries are insufficient.
00058             //
00059             // For example, with 65K worth of entries the odds are roughly
00060             // 1 in 10-billion.
00061             // For 6 million entries, the odds would still be 1 in a million
00062             // (If the num entries is greater than ~20 million, then all 128
00063             // bits of hash should be used)
00064 
00065             const uint64_t hash = iAttr.getHash().uint64();
00066 
00067             if (m_maxNumInvalidKeys > 0)
00068             {
00069                 // lock for our search in the invalid set
00070                 std::lock_guard<std::mutex> lock(m_invalidKeysMutex);
00071 
00072                 if (m_invalidKeys.find(hash) != m_invalidKeys.end())
00073                 {
00074                     return IMPLPtr();
00075                 }
00076             }
00077 
00078             if (m_maxNumEntries > 0)
00079             {
00080                 // lock for our search in the cache
00081                 std::lock_guard<std::mutex> lock(m_entriesMutex);
00082 
00083                 const auto it = m_entries.find(hash);
00084                 if (it != m_entries.end())
00085                 {
00086                     return it->second;
00087                 }
00088             }
00089 
00090             IMPLPtr val = createValue(iAttr);
00091             if (val && m_maxNumEntries > 0)
00092             {
00093                 std::lock_guard<std::mutex> lock(m_entriesMutex);
00094                 if (!m_entries.count(hash))
00095                 {
00096                     if (m_entries.size() == m_maxNumEntries)
00097                     {
00098                         const std::size_t idx =
00099                             std::rand() % m_entriesVector.size();
00100                         const uint64_t keyToDrop = m_entriesVector[idx];
00101                         m_entriesVector[idx] = hash;
00102                         m_entries.erase(keyToDrop);
00103                     }
00104                     else
00105                     {
00106                         m_entriesVector.push_back(hash);
00107                     }
00108                     m_entries.emplace(hash, val);
00109                 }
00110             }
00111             else if (!val && m_maxNumInvalidKeys > 0)
00112             {
00113                 std::lock_guard<std::mutex> lock(m_invalidKeysMutex);
00114                 if (!m_invalidKeys.count(hash))
00115                 {
00116                     if (m_invalidKeys.size() == m_maxNumInvalidKeys)
00117                     {
00118                         const std::size_t idx =
00119                             std::rand() % m_invalidKeysVector.size();
00120                         const uint64_t keyToDrop = m_invalidKeysVector[idx];
00121                         m_invalidKeysVector[idx] = hash;
00122                         m_invalidKeys.erase(keyToDrop);
00123                     }
00124                     else
00125                     {
00126                         m_invalidKeysVector.push_back(hash);
00127                     }
00128                     m_invalidKeys.emplace(hash);
00129                 }
00130             }
00131             return val;
00132         }
00133 
00134         void clear()
00135         {
00136             {
00137                 std::lock_guard<std::mutex> lock(m_entriesMutex);
00138                 m_entries.clear();
00139                 m_entriesVector.clear();
00140             }
00141 
00142             {
00143                 std::lock_guard<std::mutex> lock(m_invalidKeysMutex);
00144                 m_invalidKeys.clear();
00145                 m_invalidKeysVector.clear();
00146             }
00147         }
00148 
00149     protected:
00150         virtual IMPLPtr createValue(const FnAttribute::Attribute& iAttr) = 0;
00151 
00152     private:
00153         const std::size_t m_maxNumEntries;
00154         const std::size_t m_maxNumInvalidKeys;
00155 
00156         KeyMapType m_entries;
00157         KeySetType m_invalidKeys;
00158 
00159         std::vector<uint64_t> m_entriesVector;
00160         std::vector<uint64_t> m_invalidKeysVector;
00161 
00162         std::mutex m_invalidKeysMutex;
00163         std::mutex m_entriesMutex;
00164     };
00165 
00166 #ifndef ATTRIBUTEKEYEDCACHE_NO_TBB
00167     template <class T, class PointerT =
00168                            typename std::shared_ptr<T>>
00169     class AttributeKeyedCacheLockFree
00170     {
00171     public:
00172         typedef PointerT IMPLPtr;
00173 
00174         AttributeKeyedCacheLockFree(
00175             const std::size_t maxNumEntries = 0xffffffff,
00176             const std::size_t maxNumInvalidKeys = 0xffffffff)
00177             : m_maxNumEntries(maxNumEntries),
00178               m_maxNumInvalidKeys(maxNumInvalidKeys)
00179         {
00180         }
00181 
00182         virtual ~AttributeKeyedCacheLockFree() {}
00183 
00184         IMPLPtr getValue(const FnAttribute::Attribute& iAttr)
00185         {
00186             // For these purposes, good enough to use a simple 64-bit hash.
00187             // The rationale is that AttributeKeyedCache should not grow to
00188             // the point where 2**64th hash entries are insufficient.
00189             //
00190             // For example, with 65K worth of entries the odds are roughly
00191             // 1 in 10-billion.
00192             // For 6 million entries, the odds would still be 1 in a million
00193             // (If the num entries is greater than ~20 million, then all 128
00194             // bits of hash should be used)
00195 
00196             const uint64_t hash = iAttr.getHash().uint64();
00197 
00198             if (m_maxNumInvalidKeys > 0)
00199             {
00200                 typename decltype(m_invalidKeys)::const_accessor acc;
00201                 if (m_invalidKeys.find(acc, hash))
00202                 {
00203                     return {};
00204                 }
00205             }
00206 
00207             if (m_maxNumEntries > 0)
00208             {
00209                 typename decltype(m_entries)::const_accessor acc;
00210                 if (m_entries.find(acc, hash))
00211                 {
00212                     return acc->second;
00213                 }
00214             }
00215 
00216             IMPLPtr val = createValue(iAttr);
00217             if (val && m_maxNumEntries > 0)
00218             {
00219                 insert<IMPLPtr, typename decltype(m_entries)::accessor>(
00220                     hash, val, m_maxNumEntries, m_numEntries, m_entries,
00221                     m_entriesQueue);
00222             }
00223             else if (!val && m_maxNumInvalidKeys > 0)
00224             {
00225                 insert<bool, typename decltype(m_invalidKeys)::accessor>(
00226                     hash, false, m_maxNumInvalidKeys, m_numInvalidKeys,
00227                     m_invalidKeys, m_invalidKeysQueue);
00228             }
00229             return val;
00230         }
00231 
00232         void clear()
00233         {
00234             if (m_numEntries.exchange(0))
00235             {
00236                 m_entries.clear();
00237                 m_entriesQueue.clear();
00238             }
00239 
00240             if (m_numInvalidKeys.exchange(0))
00241             {
00242                 m_invalidKeys.clear();
00243                 m_invalidKeysQueue.clear();
00244             }
00245         }
00246 
00247     protected:
00248         virtual IMPLPtr createValue(const FnAttribute::Attribute& iAttr) = 0;
00249 
00250     private:
00251         template <typename ValueT, typename AccessorT>
00252         static void insert(const uint64_t key,
00253                            const ValueT& value,
00254                            const std::size_t& maxNumEntries,
00255                            std::atomic_size_t& numEntries,
00256                            tbb::concurrent_hash_map<uint64_t, ValueT>& entries,
00257                            tbb::concurrent_queue<uint64_t>& entriesQueue)
00258         {
00259             AccessorT acc;
00260             if (entries.insert(acc, key))
00261             {
00262                 acc->second = value;
00263                 acc.release();
00264 
00265                 const bool dropKey = ++numEntries > maxNumEntries;
00266                 if (dropKey)
00267                 {
00268                     uint64_t keyToDrop = 0;
00269                     const bool retrieved = entriesQueue.try_pop(keyToDrop);
00270                     (void)retrieved;
00271                     assert(retrieved);
00272                     const bool erased = entries.erase(keyToDrop);
00273                     (void)erased;
00274                     assert(erased);
00275                     --numEntries;
00276                 }
00277 
00278                 entriesQueue.push(key);
00279             }
00280         }
00281 
00282     private:
00283         const std::size_t m_maxNumEntries;
00284         const std::size_t m_maxNumInvalidKeys;
00285 
00286         std::atomic_size_t m_numEntries{0};
00287         std::atomic_size_t m_numInvalidKeys{0};
00288 
00289         tbb::concurrent_hash_map<uint64_t, IMPLPtr> m_entries;
00290         tbb::concurrent_hash_map<uint64_t, bool /*unused*/> m_invalidKeys;
00291 
00292         tbb::concurrent_queue<uint64_t> m_entriesQueue;
00293         tbb::concurrent_queue<uint64_t> m_invalidKeysQueue;
00294     };
00295 #endif
00296 
00350     template <class T,
00351               class PointerT =
00352                   typename std::shared_ptr<T>,
00353 #ifdef ATTRIBUTEKEYEDCACHE_NO_TBB
00354               class Base = AttributeKeyedCacheMutex<T, PointerT> >
00355 #else
00356               class Base = AttributeKeyedCacheLockFree<T, PointerT> >
00357 #endif
00358     class AttributeKeyedCache : public Base
00359     {
00360     public:
00361         typedef PointerT IMPLPtr;
00362 
00375         AttributeKeyedCache(const std::size_t maxNumEntries = 0xffffffff,
00376                             const std::size_t maxNumInvalidKeys = 0xffffffff)
00377             : Base(maxNumEntries, maxNumInvalidKeys)
00378         {
00379         }
00380 
00381         virtual ~AttributeKeyedCache() {}
00382 
00392         IMPLPtr getValue(const FnAttribute::Attribute& iAttr)
00393         {
00394             return Base::getValue(iAttr);
00395         }
00396 
00400         void clear() { Base::clear(); }
00401 
00402     protected:
00419         virtual IMPLPtr createValue(const FnAttribute::Attribute& iAttr) = 0;
00420     };
00421 
00425 }
00426 FNGEOLIBUTIL_NAMESPACE_EXIT
00427 
00428 #endif  // INCLUDED_FNGEOLIBUTIL_ATTRIBUTEKEYEDCACHE_H
 All Classes Functions Variables Typedefs Enumerations Enumerator