hostsupport/hostopenvg/src/src/sfFunctionCache.h
branchbug235_bringup_0
changeset 54 067180f57b12
parent 53 c2ef9095503a
child 55 09263774e342
equal deleted inserted replaced
53:c2ef9095503a 54:067180f57b12
     1 /* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     2  *
       
     3  * Permission is hereby granted, free of charge, to any person obtaining a
       
     4  * copy of this software and /or associated documentation files
       
     5  * (the "Materials "), to deal in the Materials without restriction,
       
     6  * including without limitation the rights to use, copy, modify, merge,
       
     7  * publish, distribute, sublicense, and/or sell copies of the Materials,
       
     8  * and to permit persons to whom the Materials are furnished to do so,
       
     9  * subject to the following conditions:
       
    10  *
       
    11  * The above copyright notice and this permission notice shall be included
       
    12  * in all copies or substantial portions of the Materials.
       
    13  *
       
    14  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       
    15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       
    16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
       
    17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
       
    18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
       
    19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
       
    20  * THE USE OR OTHER DEALINGS IN THE MATERIALS.
       
    21  */
       
    22 
       
    23 #ifndef __SFFUNCTIONCACHE_H
       
    24 #define __SFFUNCTIONCACHE_H
       
    25 
       
    26 // (LRU) Cache for compiled pixelpipe functions. Never takes ownership of
       
    27 // any of the objects.
       
    28 // \todo LRU might not be the best strategy or the best strategy might
       
    29 // depend on the use-case -> create more of these.
       
    30 
       
    31 #include "riArray.h"
       
    32 
       
    33 #if defined(__unix__)
       
    34 #   include <pthread.h>
       
    35 #else
       
    36 #   include <windows.h>
       
    37 #endif
       
    38 
       
    39 #include "llvm/ExecutionEngine/ExecutionEngine.h"
       
    40 #include "llvm/Module.h"
       
    41 
       
    42 namespace llvm {
       
    43     class Function;
       
    44 }
       
    45 
       
    46 namespace OpenVGRI {
       
    47 
       
    48 template<class HashClass> class FunctionCache 
       
    49 {
       
    50 private:
       
    51     enum { IMPLEMENTATION_MAX_CACHE_ENTRIES = 1024 };
       
    52     //enum { MAX_GLOBAL_TIME = 10000};
       
    53     enum { MAX_GLOBAL_TIME = RI_UINT32_MAX };
       
    54 
       
    55     struct CacheEntry 
       
    56     {
       
    57         CacheEntry() : refCount(1) {}
       
    58         CacheEntry(HashClass aHash, ::llvm::Function* aFunc, ::llvm::GlobalVariable* aConst, RIuint32 theUT) : refCount(1) {hash = aHash; llvmFunction = aFunc; llvmConstant = aConst; ut = theUT;}
       
    59         bool operator==(const CacheEntry& rhs) const { return hash == rhs.hash; }
       
    60         bool operator<(const CacheEntry& rhs) const { return ut < rhs.ut; } // Sort by time of usage.
       
    61 
       
    62         HashClass           hash;
       
    63         ::llvm::Function*   llvmFunction;
       
    64         ::llvm::GlobalVariable*   llvmConstant;
       
    65         RIuint32            ut;
       
    66         RIint32             refCount;
       
    67     };
       
    68 
       
    69 public:
       
    70     typedef CacheEntry* EntryHandle;
       
    71 
       
    72 public:
       
    73     FunctionCache(int nMaxEntries) :
       
    74         m_time(0)
       
    75     {
       
    76         // Limit so that if the cache is too large, you must optimize the implementation.
       
    77         // Also note that the optimized pixel pipes are most likely small, so it would 
       
    78         // be better to have a fast cache and a lot of entries!
       
    79         // \note A simple optimization is to sort the usage time sort order and remove the last
       
    80         // item in the array (instead of the first).
       
    81         RI_ASSERT(nMaxEntries > 0 && nMaxEntries < IMPLEMENTATION_MAX_CACHE_ENTRIES); 
       
    82         m_nMaxEntries = nMaxEntries;
       
    83         m_entries.reserve(nMaxEntries);
       
    84     }
       
    85 
       
    86     ~FunctionCache() 
       
    87     {
       
    88         for (int i = 0; i < m_entries.size(); i++)
       
    89         {
       
    90             clearEntry(m_entries[i]);
       
    91         }
       
    92     }
       
    93     
       
    94     // This info is needed for the module to remove functions and deallocate executable
       
    95     // functions:
       
    96     void setLLVMInterface(::llvm::ExecutionEngine* ee, ::llvm::Module* module)
       
    97     {
       
    98         m_executionEngine = ee;
       
    99         m_module = module;
       
   100     }
       
   101 
       
   102     // \todo If we never need the entry index, the locking can be
       
   103     // simplified a lot.
       
   104     // Must lock the cache during this operation!
       
   105     EntryHandle findCachedItemByHash(const HashClass& hash)
       
   106     {
       
   107         acquireMutex();
       
   108         int i = findCachedItemIndexByHash(hash, true);
       
   109         if (i == -1)
       
   110         {
       
   111             releaseMutex();
       
   112             return NULL;
       
   113         }
       
   114         EntryHandle handle = &m_entries[i];
       
   115         releaseMutex();
       
   116         
       
   117         return handle;
       
   118     }
       
   119 
       
   120     /**
       
   121      * \brief   Caches a function. Sets the reference count to 1
       
   122      * \return  EntryHandle != NULL on success.
       
   123      * \todo    The cache must be locked during the operation.
       
   124      */
       
   125     EntryHandle cacheFunction(HashClass hash, ::llvm::Function* function, ::llvm::GlobalVariable* constant)
       
   126     {
       
   127         acquireMutex();
       
   128         RI_ASSERT(findCachedItemIndexByHash(hash) == -1);
       
   129 
       
   130         if (m_entries.size() == m_nMaxEntries)
       
   131         {
       
   132             if (!removeLRU())
       
   133             {
       
   134                 releaseMutex();
       
   135                 return NULL;
       
   136             }
       
   137         }
       
   138 
       
   139         m_entries.push_back(CacheEntry(hash, function, constant, m_time));
       
   140         
       
   141         RI_ASSERT(m_entries.size() > 0);
       
   142         EntryHandle ret = &m_entries[m_entries.size()-1];
       
   143         incrementGlobalTime();
       
   144 
       
   145         releaseMutex();
       
   146         return ret;
       
   147     }
       
   148 
       
   149     ::llvm::Function* getFunction(EntryHandle handle)
       
   150     {
       
   151         return handle->llvmFunction;
       
   152     }
       
   153 
       
   154     // \note Does not remove the function from cache!
       
   155     void releaseEntry(EntryHandle handle)
       
   156     {
       
   157         RI_ASSERT(handle->refCount > 0);
       
   158         handle->refCount--;
       
   159     }
       
   160 
       
   161 private:
       
   162     void incrementGlobalTime()
       
   163     {
       
   164         m_time++;
       
   165         if (m_time == MAX_GLOBAL_TIME)
       
   166             rebaseUsageTime();
       
   167     }
       
   168 
       
   169     void incrementAccessTime(CacheEntry &entry)
       
   170     {
       
   171         entry.ut = m_time;
       
   172         incrementGlobalTime();
       
   173     }
       
   174 
       
   175     int findCachedItemIndexByHash(const HashClass& hash, bool reserve = false)
       
   176     {
       
   177         // \note Could just overload operator== from entry and use the Array.find function.
       
   178         for (int i = 0; i < m_entries.size(); i++)
       
   179         {
       
   180             if (m_entries[i].hash == hash)
       
   181             {
       
   182                 if (reserve)
       
   183                 {
       
   184                     incrementAccessTime(m_entries[i]);
       
   185                     m_entries[i].refCount++;
       
   186                 }
       
   187                 return i;
       
   188             }
       
   189         }
       
   190         return -1;
       
   191     }
       
   192 
       
   193     void clearEntry(CacheEntry& entry)
       
   194     {
       
   195         m_executionEngine->freeMachineCodeForFunction(entry.llvmFunction);
       
   196         entry.llvmFunction->eraseFromParent();
       
   197         //entry.llvmConstant->eraseFromParent();
       
   198     }
       
   199 
       
   200     /**
       
   201      * \return  true if LRU item was successfully removed, false otherwise.
       
   202      * \note    Could try other pipes, but it is unlikely that the cache gets filled
       
   203      *          so soon that the blit for the least recently used blit has not been
       
   204      *          released.
       
   205      * \todo    Implement drop of other cache-entries?
       
   206      */
       
   207     bool removeLRU() 
       
   208     {
       
   209         // \note This is pretty inefficient for many cache size:
       
   210         // After first LRU removal, the cache is almost sorted anyway, so
       
   211         // more efficient solution should be implemented.
       
   212         //
       
   213         m_entries.sort();
       
   214 
       
   215         if (m_entries[0].refCount > 0)
       
   216             return false;
       
   217 
       
   218         clearEntry(m_entries[0]);
       
   219         m_entries.remove(m_entries[0]);
       
   220 
       
   221         return true;
       
   222     }
       
   223 
       
   224     void rebaseUsageTime()
       
   225     {
       
   226         RIuint32 i;
       
   227         m_entries.sort();
       
   228         RI_ASSERT(m_entries.size() > 0);
       
   229         for(i = 0; i < (RIuint32)m_entries.size(); i++)
       
   230         {
       
   231             m_entries[i].ut = i;
       
   232         };
       
   233         m_time = i;
       
   234     }
       
   235 
       
   236     static void acquireMutex();
       
   237     static void releaseMutex();
       
   238 
       
   239 private:
       
   240     ::llvm::Module              *m_module;
       
   241     ::llvm::ExecutionEngine     *m_executionEngine;
       
   242 
       
   243     RIuint32            m_time;
       
   244     Array <CacheEntry>  m_entries;
       
   245     int                 m_nMaxEntries;
       
   246 
       
   247     static bool             s_mutexInitialized;
       
   248 #if defined(__unix__)
       
   249     static pthread_mutex_t  s_mutex;
       
   250 #else
       
   251     static CRITICAL_SECTION s_mutex;
       
   252 #endif
       
   253 };
       
   254 
       
   255 template<class HashClass>
       
   256 bool FunctionCache<HashClass>::s_mutexInitialized = false;
       
   257 
       
   258 #if defined(__unix__)
       
   259 template<class HashClass>
       
   260 pthread_mutex_t FunctionCache<HashClass>::s_mutex;
       
   261 #else
       
   262 template<class HashClass>
       
   263 CRITICAL_SECTION FunctionCache<HashClass>::s_mutex;
       
   264 #endif
       
   265 
       
   266 template<class HashClass>
       
   267 void FunctionCache<HashClass>::acquireMutex()
       
   268 {
       
   269     if (!s_mutexInitialized)
       
   270     {
       
   271 #if defined(__unix__)
       
   272         int ret;
       
   273         pthread_mutexattr_t attr;
       
   274         ret = pthread_mutexattr_init(&attr);	//initially not locked
       
   275         RI_ASSERT(!ret);	//check that there aren't any errors
       
   276         ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);	//count the number of recursive locks
       
   277         RI_ASSERT(!ret);	//check that there aren't any errors
       
   278         ret = pthread_mutex_init(&s_mutex, &attr);
       
   279         pthread_mutexattr_destroy(&attr);
       
   280         RI_ASSERT(!ret);	//check that there aren't more errors
       
   281 #else
       
   282         ::InitializeCriticalSection(&s_mutex);
       
   283 #endif
       
   284         s_mutexInitialized = true;
       
   285     }
       
   286 #if defined(__unix__)
       
   287     int ret = pthread_mutex_lock(&s_mutex);
       
   288     RI_ASSERT(!ret);
       
   289 #else
       
   290     ::EnterCriticalSection(&s_mutex);
       
   291 #endif
       
   292 }
       
   293 
       
   294 template<class HashClass>
       
   295 void FunctionCache<HashClass>::releaseMutex()
       
   296 {
       
   297     RI_ASSERT(s_mutexInitialized);
       
   298 #if defined(__unix__)
       
   299     int ret = pthread_mutex_unlock(&s_mutex);
       
   300     RI_ASSERT(!ret);
       
   301 #else
       
   302     ::LeaveCriticalSection(&s_mutex);
       
   303 #endif
       
   304 }
       
   305 
       
   306 }
       
   307 
       
   308 
       
   309 #endif
       
   310