diff -r 39e5f73667ba -r c2ef9095503a hostsupport/hostopenvg/src/sfFunctionCache.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hostsupport/hostopenvg/src/sfFunctionCache.h Wed Oct 06 17:59:01 2010 +0100 @@ -0,0 +1,310 @@ +/* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + */ + +#ifndef __SFFUNCTIONCACHE_H +#define __SFFUNCTIONCACHE_H + +// (LRU) Cache for compiled pixelpipe functions. Never takes ownership of +// any of the objects. +// \todo LRU might not be the best strategy or the best strategy might +// depend on the use-case -> create more of these. + +#include "riArray.h" + +#if defined(__unix__) +# include +#else +# include +#endif + +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Module.h" + +namespace llvm { + class Function; +} + +namespace OpenVGRI { + +template class FunctionCache +{ +private: + enum { IMPLEMENTATION_MAX_CACHE_ENTRIES = 1024 }; + //enum { MAX_GLOBAL_TIME = 10000}; + enum { MAX_GLOBAL_TIME = RI_UINT32_MAX }; + + struct CacheEntry + { + CacheEntry() : refCount(1) {} + CacheEntry(HashClass aHash, ::llvm::Function* aFunc, ::llvm::GlobalVariable* aConst, RIuint32 theUT) : refCount(1) {hash = aHash; llvmFunction = aFunc; llvmConstant = aConst; ut = theUT;} + bool operator==(const CacheEntry& rhs) const { return hash == rhs.hash; } + bool operator<(const CacheEntry& rhs) const { return ut < rhs.ut; } // Sort by time of usage. + + HashClass hash; + ::llvm::Function* llvmFunction; + ::llvm::GlobalVariable* llvmConstant; + RIuint32 ut; + RIint32 refCount; + }; + +public: + typedef CacheEntry* EntryHandle; + +public: + FunctionCache(int nMaxEntries) : + m_time(0) + { + // Limit so that if the cache is too large, you must optimize the implementation. + // Also note that the optimized pixel pipes are most likely small, so it would + // be better to have a fast cache and a lot of entries! + // \note A simple optimization is to sort the usage time sort order and remove the last + // item in the array (instead of the first). + RI_ASSERT(nMaxEntries > 0 && nMaxEntries < IMPLEMENTATION_MAX_CACHE_ENTRIES); + m_nMaxEntries = nMaxEntries; + m_entries.reserve(nMaxEntries); + } + + ~FunctionCache() + { + for (int i = 0; i < m_entries.size(); i++) + { + clearEntry(m_entries[i]); + } + } + + // This info is needed for the module to remove functions and deallocate executable + // functions: + void setLLVMInterface(::llvm::ExecutionEngine* ee, ::llvm::Module* module) + { + m_executionEngine = ee; + m_module = module; + } + + // \todo If we never need the entry index, the locking can be + // simplified a lot. + // Must lock the cache during this operation! + EntryHandle findCachedItemByHash(const HashClass& hash) + { + acquireMutex(); + int i = findCachedItemIndexByHash(hash, true); + if (i == -1) + { + releaseMutex(); + return NULL; + } + EntryHandle handle = &m_entries[i]; + releaseMutex(); + + return handle; + } + + /** + * \brief Caches a function. Sets the reference count to 1 + * \return EntryHandle != NULL on success. + * \todo The cache must be locked during the operation. + */ + EntryHandle cacheFunction(HashClass hash, ::llvm::Function* function, ::llvm::GlobalVariable* constant) + { + acquireMutex(); + RI_ASSERT(findCachedItemIndexByHash(hash) == -1); + + if (m_entries.size() == m_nMaxEntries) + { + if (!removeLRU()) + { + releaseMutex(); + return NULL; + } + } + + m_entries.push_back(CacheEntry(hash, function, constant, m_time)); + + RI_ASSERT(m_entries.size() > 0); + EntryHandle ret = &m_entries[m_entries.size()-1]; + incrementGlobalTime(); + + releaseMutex(); + return ret; + } + + ::llvm::Function* getFunction(EntryHandle handle) + { + return handle->llvmFunction; + } + + // \note Does not remove the function from cache! + void releaseEntry(EntryHandle handle) + { + RI_ASSERT(handle->refCount > 0); + handle->refCount--; + } + +private: + void incrementGlobalTime() + { + m_time++; + if (m_time == MAX_GLOBAL_TIME) + rebaseUsageTime(); + } + + void incrementAccessTime(CacheEntry &entry) + { + entry.ut = m_time; + incrementGlobalTime(); + } + + int findCachedItemIndexByHash(const HashClass& hash, bool reserve = false) + { + // \note Could just overload operator== from entry and use the Array.find function. + for (int i = 0; i < m_entries.size(); i++) + { + if (m_entries[i].hash == hash) + { + if (reserve) + { + incrementAccessTime(m_entries[i]); + m_entries[i].refCount++; + } + return i; + } + } + return -1; + } + + void clearEntry(CacheEntry& entry) + { + m_executionEngine->freeMachineCodeForFunction(entry.llvmFunction); + entry.llvmFunction->eraseFromParent(); + //entry.llvmConstant->eraseFromParent(); + } + + /** + * \return true if LRU item was successfully removed, false otherwise. + * \note Could try other pipes, but it is unlikely that the cache gets filled + * so soon that the blit for the least recently used blit has not been + * released. + * \todo Implement drop of other cache-entries? + */ + bool removeLRU() + { + // \note This is pretty inefficient for many cache size: + // After first LRU removal, the cache is almost sorted anyway, so + // more efficient solution should be implemented. + // + m_entries.sort(); + + if (m_entries[0].refCount > 0) + return false; + + clearEntry(m_entries[0]); + m_entries.remove(m_entries[0]); + + return true; + } + + void rebaseUsageTime() + { + RIuint32 i; + m_entries.sort(); + RI_ASSERT(m_entries.size() > 0); + for(i = 0; i < (RIuint32)m_entries.size(); i++) + { + m_entries[i].ut = i; + }; + m_time = i; + } + + static void acquireMutex(); + static void releaseMutex(); + +private: + ::llvm::Module *m_module; + ::llvm::ExecutionEngine *m_executionEngine; + + RIuint32 m_time; + Array m_entries; + int m_nMaxEntries; + + static bool s_mutexInitialized; +#if defined(__unix__) + static pthread_mutex_t s_mutex; +#else + static CRITICAL_SECTION s_mutex; +#endif +}; + +template +bool FunctionCache::s_mutexInitialized = false; + +#if defined(__unix__) +template +pthread_mutex_t FunctionCache::s_mutex; +#else +template +CRITICAL_SECTION FunctionCache::s_mutex; +#endif + +template +void FunctionCache::acquireMutex() +{ + if (!s_mutexInitialized) + { +#if defined(__unix__) + int ret; + pthread_mutexattr_t attr; + ret = pthread_mutexattr_init(&attr); //initially not locked + RI_ASSERT(!ret); //check that there aren't any errors + ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //count the number of recursive locks + RI_ASSERT(!ret); //check that there aren't any errors + ret = pthread_mutex_init(&s_mutex, &attr); + pthread_mutexattr_destroy(&attr); + RI_ASSERT(!ret); //check that there aren't more errors +#else + ::InitializeCriticalSection(&s_mutex); +#endif + s_mutexInitialized = true; + } +#if defined(__unix__) + int ret = pthread_mutex_lock(&s_mutex); + RI_ASSERT(!ret); +#else + ::EnterCriticalSection(&s_mutex); +#endif +} + +template +void FunctionCache::releaseMutex() +{ + RI_ASSERT(s_mutexInitialized); +#if defined(__unix__) + int ret = pthread_mutex_unlock(&s_mutex); + RI_ASSERT(!ret); +#else + ::LeaveCriticalSection(&s_mutex); +#endif +} + +} + + +#endif +