Redesign BlockCache's age tracking to run in constant time

Speeds up spectrum painting by 40-90% depending on how much of the audio
data is being read from the cache.

Originally committed to SVN as r6488.
This commit is contained in:
Thomas Goyne 2012-02-20 05:15:10 +00:00
parent ffa7e70f5c
commit 53c2c8821b

View file

@ -37,6 +37,7 @@
#ifndef AGI_PRE #ifndef AGI_PRE
#include <algorithm> #include <algorithm>
#include <list>
#include <vector> #include <vector>
#endif #endif
@ -103,8 +104,10 @@ class DataBlockCache {
/// DOCME /// DOCME
struct MacroBlock { struct MacroBlock {
/// How long has it been since this macroblock was accessed? /// This macroblock's position in the age list
int age; /// Is valid iff blocks.size() > 0
typename std::list<MacroBlock*>::iterator position;
/// The blocks contained in the macroblock /// The blocks contained in the macroblock
BlockArray blocks; BlockArray blocks;
}; };
@ -115,6 +118,12 @@ class DataBlockCache {
/// The data in the cache /// The data in the cache
MacroBlockArray data; MacroBlockArray data;
/// Type of a list of macro blocks
typedef std::list<MacroBlock*> AgeList;
/// The data sorted by how long it's been since they were used
AgeList age;
/// Number of blocks per macroblock /// Number of blocks per macroblock
size_t macroblock_size; size_t macroblock_size;
@ -124,13 +133,13 @@ class DataBlockCache {
/// Factory object for blocks /// Factory object for blocks
BlockFactoryT factory; BlockFactoryT factory;
/// Used in sorting the macroblocks by age
static bool comp_age(const MacroBlock *lft, const MacroBlock *rgt) { return lft->age < rgt->age; }
/// @brief Dispose of all blocks in a macroblock and mark it empty /// @brief Dispose of all blocks in a macroblock and mark it empty
/// @param mb_index Index of macroblock to clear /// @param mb_index Index of macroblock to clear
void KillMacroBlock(MacroBlock &mb) void KillMacroBlock(MacroBlock &mb)
{ {
if (mb.blocks.empty())
return;
for (size_t bi = 0; bi < mb.blocks.size(); ++bi) for (size_t bi = 0; bi < mb.blocks.size(); ++bi)
{ {
BlockT *b = mb.blocks[bi]; BlockT *b = mb.blocks[bi];
@ -139,6 +148,7 @@ class DataBlockCache {
} }
mb.blocks.clear(); mb.blocks.clear();
age.erase(mb.position);
} }
public: public:
@ -204,30 +214,19 @@ public:
return; return;
} }
// Get a list of macro blocks sorted by access count
std::vector<MacroBlock*> access_data;
access_data.reserve(data.size());
// For whatever reason, G++ pukes if I try using iterators here...
for (size_t mbi = 0; mbi != data.size(); ++mbi)
{
if (data[mbi].blocks.size())
access_data.push_back(&data[mbi]);
}
sort(access_data.begin(), access_data.end(), comp_age);
// Sum up data size until we hit the max // Sum up data size until we hit the max
size_t cur_size = 0; size_t cur_size = 0;
size_t block_size = factory.GetBlockSize(); size_t block_size = factory.GetBlockSize();
size_t mbi = 0; typename AgeList::iterator it = age.begin();
for (; mbi < access_data.size() && cur_size < max_size; ++mbi) for (; it != age.end() && cur_size < max_size; ++it)
{ {
BlockArray &ba = access_data[mbi]->blocks; BlockArray &ba = (*it)->blocks;
cur_size += (ba.size() - std::count(ba.begin(), ba.end(), (BlockT*)0)) * block_size; cur_size += (ba.size() - std::count(ba.begin(), ba.end(), (BlockT*)0)) * block_size;
} }
// Hit max, clear all remaining blocks // Hit max, clear all remaining blocks
for (++mbi; mbi < access_data.size(); ++mbi) for (; it != age.end(); ++it)
{ {
KillMacroBlock(*access_data[mbi]); KillMacroBlock(**it);
} }
} }
@ -243,19 +242,20 @@ public:
size_t mbi = i >> MacroblockExponent; size_t mbi = i >> MacroblockExponent;
assert(mbi < data.size()); assert(mbi < data.size());
// Age all of the other macroblocks
for (size_t idx = 0; idx < data.size(); ++idx)
{
data[idx].age += 1;
}
MacroBlock &mb = data[mbi]; MacroBlock &mb = data[mbi];
mb.age = 0;
if (mb.blocks.size() == 0) if (mb.blocks.size() == 0)
{ {
mb.blocks.resize(macroblock_size); mb.blocks.resize(macroblock_size);
} }
else
{
age.erase(mb.position);
}
// Move this macroblock to the front of the age list
age.push_front(&mb);
mb.position = age.begin();
size_t block_index = i & macroblock_index_mask; size_t block_index = i & macroblock_index_mask;
assert(block_index < mb.blocks.size()); assert(block_index < mb.blocks.size());