From 53c2c8821b86b6d441c7fa8228ff622919adb21d Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Mon, 20 Feb 2012 05:15:10 +0000 Subject: [PATCH] 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. --- aegisub/src/block_cache.h | 56 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/aegisub/src/block_cache.h b/aegisub/src/block_cache.h index 53eccafae..5e7134ba3 100644 --- a/aegisub/src/block_cache.h +++ b/aegisub/src/block_cache.h @@ -37,6 +37,7 @@ #ifndef AGI_PRE #include +#include #include #endif @@ -103,8 +104,10 @@ class DataBlockCache { /// DOCME struct MacroBlock { - /// How long has it been since this macroblock was accessed? - int age; + /// This macroblock's position in the age list + /// Is valid iff blocks.size() > 0 + typename std::list::iterator position; + /// The blocks contained in the macroblock BlockArray blocks; }; @@ -115,6 +118,12 @@ class DataBlockCache { /// The data in the cache MacroBlockArray data; + /// Type of a list of macro blocks + typedef std::list AgeList; + + /// The data sorted by how long it's been since they were used + AgeList age; + /// Number of blocks per macroblock size_t macroblock_size; @@ -124,13 +133,13 @@ class DataBlockCache { /// Factory object for blocks 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 /// @param mb_index Index of macroblock to clear void KillMacroBlock(MacroBlock &mb) { + if (mb.blocks.empty()) + return; + for (size_t bi = 0; bi < mb.blocks.size(); ++bi) { BlockT *b = mb.blocks[bi]; @@ -139,6 +148,7 @@ class DataBlockCache { } mb.blocks.clear(); + age.erase(mb.position); } public: @@ -204,30 +214,19 @@ public: return; } - // Get a list of macro blocks sorted by access count - std::vector 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 size_t cur_size = 0; size_t block_size = factory.GetBlockSize(); - size_t mbi = 0; - for (; mbi < access_data.size() && cur_size < max_size; ++mbi) + typename AgeList::iterator it = age.begin(); + 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; } // 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; 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]; - mb.age = 0; if (mb.blocks.size() == 0) { 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; assert(block_index < mb.blocks.size());