From 39a59d68061710db886bc00cb571c6feb8cbdc65 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 2 Jun 2012 13:45:57 +0000 Subject: [PATCH] Added a cHeiGenCache object for caching generated heightmaps. World generation is now about twice as fast as before Rev 535 :) git-svn-id: http://mc-server.googlecode.com/svn/trunk@538 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/HeiGen.cpp | 90 ++++++++++++++++++++++++++++++++++++++ source/HeiGen.h | 36 +++++++++++++++ source/cChunkGenerator.cpp | 17 +++++++ 3 files changed, 143 insertions(+) diff --git a/source/HeiGen.cpp b/source/HeiGen.cpp index 6513d273..b2e9e48c 100644 --- a/source/HeiGen.cpp +++ b/source/HeiGen.cpp @@ -25,6 +25,96 @@ void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenCache: + +cHeiGenCache::cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize) : + m_HeiGenToCache(a_HeiGenToCache), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cHeiGenCache::~cHeiGenCache() +{ + delete m_CacheData; + delete m_CacheOrder; + delete m_HeiGenToCache; +} + + + + + +void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("HeiGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap)); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_HeiGenToCache->GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap)); + m_CacheData[Idx].m_ChunkX = a_ChunkX; + m_CacheData[Idx].m_ChunkZ = a_ChunkZ; +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenClassic: diff --git a/source/HeiGen.h b/source/HeiGen.h index 869f1c67..d3074487 100644 --- a/source/HeiGen.h +++ b/source/HeiGen.h @@ -39,6 +39,42 @@ protected: +/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation +class cHeiGenCache : + public cTerrainHeightGen +{ +public: + cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize); // Takes ownership of a_HeiGenToCache + ~cHeiGenCache(); + +protected: + + cTerrainHeightGen * m_HeiGenToCache; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::HeightMap m_HeightMap; + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) + + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; +} ; + + + + + class cHeiGenClassic : public cTerrainHeightGen { diff --git a/source/cChunkGenerator.cpp b/source/cChunkGenerator.cpp index 1687e0e1..23d6b842 100644 --- a/source/cChunkGenerator.cpp +++ b/source/cChunkGenerator.cpp @@ -177,10 +177,12 @@ void cChunkGenerator::InitHeightGen(cIniFile & a_IniFile) HeightGenName = "classic"; } + bool CacheOffByDefault = false; if (NoCaseCompare(HeightGenName, "flat") == 0) { int Height = a_IniFile.GetValueI("Generator", "FlatHeight", 5); m_HeightGen = new cHeiGenFlat(Height); + CacheOffByDefault = true; // We're generating faster than a cache would retrieve data } else if (NoCaseCompare(HeightGenName, "classic") == 0) { @@ -201,6 +203,21 @@ void cChunkGenerator::InitHeightGen(cIniFile & a_IniFile) } m_HeightGen = new cHeiGenBiomal(m_Seed, *m_BiomeGen); } + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGINFO("Using a cache for Heightgen of size %d.", CacheSize); + m_HeightGen = new cHeiGenCache(m_HeightGen, CacheSize); + } }