forked from mia/Aegisub
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:
parent
ffa7e70f5c
commit
53c2c8821b
1 changed files with 28 additions and 28 deletions
|
@ -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());
|
||||||
|
|
Loading…
Reference in a new issue