Improvements to SimDictionary for when you have a large number of objects in the game. Under light load (i.e. under 5000 objects) this code actually runs slower that the stock simdictionary. When you exceed 5000 objects, the template class actually runs faster and more consistently.

This commit is contained in:
Vincent Gee 2014-11-04 06:30:35 -05:00
parent 378a933894
commit a91e5a2590
4 changed files with 116 additions and 17 deletions

View file

@ -23,19 +23,24 @@
#include "console/simDictionary.h"
#include "console/simBase.h"
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
extern U32 HashPointer(StringTableEntry e);
SimNameDictionary::SimNameDictionary()
{
#ifdef USE_CLASSIC_SIMDICTIONARY
hashTable = NULL;
#endif
mutex = Mutex::createMutex();
}
SimNameDictionary::~SimNameDictionary()
{
#ifdef USE_CLASSIC_SIMDICTIONARY
delete[] hashTable;
#endif
Mutex::destroyMutex(mutex);
}
@ -50,7 +55,7 @@ void SimNameDictionary::insert(SimObject* obj)
Con::warnf("Warning! You have a duplicate datablock name of %s. This can cause problems. You should rename one of them.", obj->objectName);
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
if(!hashTable)
{
hashTable = new SimObject *[DefaultTableSize];
@ -95,12 +100,15 @@ void SimNameDictionary::insert(SimObject* obj)
hashTable = newHashTable;
hashTableSize = newHashTableSize;
}
#else
root[StringTable->insert(obj->objectName)] = obj;
#endif
Mutex::unlockMutex(mutex);
}
SimObject* SimNameDictionary::find(StringTableEntry name)
{
#ifdef USE_CLASSIC_SIMDICTIONARY
// NULL is a valid lookup - it will always return NULL
if(!hashTable)
return NULL;
@ -121,6 +129,12 @@ SimObject* SimNameDictionary::find(StringTableEntry name)
Mutex::unlockMutex(mutex);
return NULL;
#else
Mutex::lockMutex(mutex);
SimObject* f = root[StringTable->insert(name)];
Mutex::unlockMutex(mutex);
return f;
#endif
}
void SimNameDictionary::remove(SimObject* obj)
@ -129,7 +143,7 @@ void SimNameDictionary::remove(SimObject* obj)
return;
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
SimObject **walk = &hashTable[HashPointer(obj->objectName) % hashTableSize];
while(*walk)
{
@ -144,7 +158,11 @@ void SimNameDictionary::remove(SimObject* obj)
}
walk = &((*walk)->nextNameObject);
}
#else
const char* name = StringTable->insert(obj->objectName);
if (root[name])
root.erase(name);
#endif
Mutex::unlockMutex(mutex);
}
@ -152,18 +170,21 @@ void SimNameDictionary::remove(SimObject* obj)
SimManagerNameDictionary::SimManagerNameDictionary()
{
#ifdef USE_CLASSIC_SIMDICTIONARY
hashTable = new SimObject *[DefaultTableSize];
hashTableSize = DefaultTableSize;
hashEntryCount = 0;
dMemset( hashTable, 0, sizeof( hashTable[ 0 ] ) * hashTableSize );
#endif
mutex = Mutex::createMutex();
}
SimManagerNameDictionary::~SimManagerNameDictionary()
{
#ifdef USE_CLASSIC_SIMDICTIONARY
delete[] hashTable;
#endif
Mutex::destroyMutex(mutex);
}
@ -173,7 +194,7 @@ void SimManagerNameDictionary::insert(SimObject* obj)
return;
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
S32 idx = HashPointer(obj->objectName) % hashTableSize;
obj->nextManagerNameObject = hashTable[idx];
hashTable[idx] = obj;
@ -209,7 +230,9 @@ void SimManagerNameDictionary::insert(SimObject* obj)
hashTable = newHashTable;
hashTableSize = newHashTableSize;
}
#else
root[StringTable->insert(obj->objectName)] = obj;
#endif
Mutex::unlockMutex(mutex);
}
@ -219,6 +242,7 @@ SimObject* SimManagerNameDictionary::find(StringTableEntry name)
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
S32 idx = HashPointer(name) % hashTableSize;
SimObject *walk = hashTable[idx];
while(walk)
@ -230,9 +254,14 @@ SimObject* SimManagerNameDictionary::find(StringTableEntry name)
}
walk = walk->nextManagerNameObject;
}
Mutex::unlockMutex(mutex);
return NULL;
#else
SimObject* f = root[StringTable->insert(name)];
Mutex::unlockMutex(mutex);
return f;
#endif
}
void SimManagerNameDictionary::remove(SimObject* obj)
@ -240,6 +269,7 @@ void SimManagerNameDictionary::remove(SimObject* obj)
if(!obj->objectName)
return;
#ifdef USE_CLASSIC_SIMDICTIONARY
Mutex::lockMutex(mutex);
SimObject **walk = &hashTable[HashPointer(obj->objectName) % hashTableSize];
@ -256,7 +286,12 @@ void SimManagerNameDictionary::remove(SimObject* obj)
}
walk = &((*walk)->nextManagerNameObject);
}
#else
const char* name = StringTable->insert(obj->objectName);
if (root[name])
root.erase(name);
#endif
Mutex::unlockMutex(mutex);
}
@ -265,7 +300,9 @@ void SimManagerNameDictionary::remove(SimObject* obj)
SimIdDictionary::SimIdDictionary()
{
#ifdef USE_CLASSIC_SIMDICTIONARY
dMemset( table, 0, sizeof( table[ 0 ] ) * DefaultTableSize );
#endif
mutex = Mutex::createMutex();
}
@ -274,22 +311,26 @@ SimIdDictionary::~SimIdDictionary()
Mutex::destroyMutex(mutex);
}
void SimIdDictionary::insert(SimObject* obj)
{
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
S32 idx = obj->getId() & TableBitMask;
obj->nextIdObject = table[idx];
AssertFatal( obj->nextIdObject != obj, "SimIdDictionary::insert - Creating Infinite Loop linking to self!" );
table[idx] = obj;
#else
root[obj->getId()] = obj;
#endif
Mutex::unlockMutex(mutex);
}
SimObject* SimIdDictionary::find(S32 id)
{
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
S32 idx = id & TableBitMask;
SimObject *walk = table[idx];
while(walk)
@ -301,22 +342,28 @@ SimObject* SimIdDictionary::find(S32 id)
}
walk = walk->nextIdObject;
}
Mutex::unlockMutex(mutex);
return NULL;
#else
SimObject* f = root[id];
Mutex::unlockMutex(mutex);
return f;
#endif
}
void SimIdDictionary::remove(SimObject* obj)
{
Mutex::lockMutex(mutex);
#ifdef USE_CLASSIC_SIMDICTIONARY
SimObject **walk = &table[obj->getId() & TableBitMask];
while(*walk && *walk != obj)
walk = &((*walk)->nextIdObject);
if(*walk)
*walk = obj->nextIdObject;
#else
root.erase(obj->getId());
#endif
Mutex::unlockMutex(mutex);
}

View file

@ -33,8 +33,37 @@
#include "platform/threads/mutex.h"
#endif
#include <string>
#include <unordered_map>
#include "TorqueConfig.h"
class SimObject;
#include "core/strings/stringFunctions.h"
struct my_hash {
inline size_t operator()(const char* val) const
{
return (long)val;
}
};
struct eqstr {
inline bool operator()(const char *s1, const char *s2) const {
return dStrcmp(s1, s2) == 0;
}
};
#ifndef USE_CLASSIC_SIMDICTIONARY
typedef std::unordered_map<const char * , SimObject*, my_hash, eqstr> StringDictDef;
typedef std::unordered_map<U32 ,SimObject*> U32DictDef;
#endif
//----------------------------------------------------------------------------
/// Map of names to SimObjects
///
@ -42,6 +71,7 @@ class SimObject;
/// for fast removal of an object given object*
class SimNameDictionary
{
#ifdef USE_CLASSIC_SIMDICTIONARY
enum
{
DefaultTableSize = 29
@ -50,9 +80,13 @@ class SimNameDictionary
SimObject **hashTable; // hash the pointers of the names...
S32 hashTableSize;
S32 hashEntryCount;
#else
StringDictDef root;
#endif
void *mutex;
public:
void insert(SimObject* obj);
void remove(SimObject* obj);
@ -64,6 +98,7 @@ public:
class SimManagerNameDictionary
{
#ifdef USE_CLASSIC_SIMDICTIONARY
enum
{
DefaultTableSize = 29
@ -73,8 +108,14 @@ class SimManagerNameDictionary
S32 hashTableSize;
S32 hashEntryCount;
void *mutex;
#else
StringDictDef root;
#endif
void *mutex;
public:
void insert(SimObject* obj);
void remove(SimObject* obj);
@ -91,13 +132,16 @@ public:
/// for fast removal of an object given object*
class SimIdDictionary
{
#ifdef USE_CLASSIC_SIMDICTIONARY
enum
{
DefaultTableSize = 4096,
TableBitMask = 4095
};
SimObject *table[DefaultTableSize];
#else
U32DictDef root;
#endif
void *mutex;
public:

View file

@ -31,6 +31,10 @@
//general, the information here is global for your entire codebase, applying
//not only to your game proper, but also to all of your tools.
//If you plan to have less than 5000 objects use the Classic SimDictionary, if you plan to have more than
//5000 objects use the new SimDictionary.
#define USE_CLASSIC_SIMDICTIONARY
/// What's the name of your application? Used in a variety of places.
#define TORQUE_APP_NAME "Empty"

View file

@ -31,6 +31,10 @@
//general, the information here is global for your entire codebase, applying
//not only to your game proper, but also to all of your tools.
//If you plan to have less than 5000 objects use the Classic SimDictionary, if you plan to have more than
//5000 objects use the new SimDictionary.
#define USE_CLASSIC_SIMDICTIONARY
/// What's the name of your application? Used in a variety of places.
#define TORQUE_APP_NAME "Full"