mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 07:09:40 +00:00
Use stable sort+unique to collect RefIDs for ESMStore records counting
The idea is to avoid std::map lookup for each CellRef. Instead generate a sequence of added and removed RefNums into a vector then order them by RefNum using a stable sort that preserves relative order of elements with the same RefNum. RefIDs are stored in a different vector to avoid std::string move ctor calls when swapping elements while sorting. Reversed iteration over added and removed RefNums for each unique RefNum is an equivalent to what map-based algorithm produces. The main benefit from sorting a vector is a data locality that means less cache misses for each access. Reduces ESMStore::countRecords perf cycles by 25%.
This commit is contained in:
parent
9938af2289
commit
1e2aae8095
2 changed files with 73 additions and 15 deletions
|
@ -1,5 +1,6 @@
|
|||
#include "esmstore.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
@ -8,12 +9,23 @@
|
|||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/misc/algorithm.hpp>
|
||||
|
||||
#include "../mwmechanics/spelllist.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void readRefs(const ESM::Cell& cell, std::map<ESM::RefNum, std::string>& refs, std::vector<ESM::ESMReader>& readers)
|
||||
struct Ref
|
||||
{
|
||||
ESM::RefNum mRefNum;
|
||||
std::size_t mRefID;
|
||||
|
||||
Ref(ESM::RefNum refNum, std::size_t refID) : mRefNum(refNum), mRefID(refID) {}
|
||||
};
|
||||
|
||||
constexpr std::size_t deletedRefID = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
void readRefs(const ESM::Cell& cell, std::vector<Ref>& refs, std::vector<std::string>& refIDs, std::vector<ESM::ESMReader>& readers)
|
||||
{
|
||||
for (size_t i = 0; i < cell.mContextList.size(); i++)
|
||||
{
|
||||
|
@ -27,24 +39,22 @@ namespace
|
|||
while(cell.getNextRef(readers[index], ref, deleted))
|
||||
{
|
||||
if(deleted)
|
||||
refs.erase(ref.mRefNum);
|
||||
refs.emplace_back(ref.mRefNum, deletedRefID);
|
||||
else if (std::find(cell.mMovedRefs.begin(), cell.mMovedRefs.end(), ref.mRefNum) == cell.mMovedRefs.end())
|
||||
{
|
||||
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
|
||||
refs[ref.mRefNum] = std::move(ref.mRefID);
|
||||
refs.emplace_back(ref.mRefNum, refIDs.size());
|
||||
refIDs.push_back(std::move(ref.mRefID));
|
||||
}
|
||||
}
|
||||
}
|
||||
for(const auto& it : cell.mLeasedRefs)
|
||||
for(const auto& [value, deleted] : cell.mLeasedRefs)
|
||||
{
|
||||
bool deleted = it.second;
|
||||
if(deleted)
|
||||
refs.erase(it.first.mRefNum);
|
||||
refs.emplace_back(value.mRefNum, deletedRefID);
|
||||
else
|
||||
{
|
||||
std::string refId = it.first.mRefID;
|
||||
Misc::StringUtils::lowerCaseInPlace(refId);
|
||||
refs[it.first.mRefNum] = std::move(refId);
|
||||
refs.emplace_back(value.mRefNum, refIDs.size());
|
||||
refIDs.push_back(value.mRefID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,14 +258,26 @@ void ESMStore::countRecords()
|
|||
{
|
||||
if(!mRefCount.empty())
|
||||
return;
|
||||
std::map<ESM::RefNum, std::string> refs;
|
||||
std::vector<Ref> refs;
|
||||
std::vector<std::string> refIDs;
|
||||
std::vector<ESM::ESMReader> readers;
|
||||
for(auto it = mCells.intBegin(); it != mCells.intEnd(); it++)
|
||||
readRefs(*it, refs, readers);
|
||||
readRefs(*it, refs, refIDs, readers);
|
||||
for(auto it = mCells.extBegin(); it != mCells.extEnd(); it++)
|
||||
readRefs(*it, refs, readers);
|
||||
for(auto& pair : refs)
|
||||
mRefCount[std::move(pair.second)]++;
|
||||
readRefs(*it, refs, refIDs, readers);
|
||||
const auto lessByRefNum = [] (const Ref& l, const Ref& r) { return l.mRefNum < r.mRefNum; };
|
||||
std::stable_sort(refs.begin(), refs.end(), lessByRefNum);
|
||||
const auto equalByRefNum = [] (const Ref& l, const Ref& r) { return l.mRefNum == r.mRefNum; };
|
||||
const auto incrementRefCount = [&] (const Ref& value)
|
||||
{
|
||||
if (value.mRefID != deletedRefID)
|
||||
{
|
||||
std::string& refId = refIDs[value.mRefID];
|
||||
Misc::StringUtils::lowerCaseInPlace(refId);
|
||||
++mRefCount[std::move(refId)];
|
||||
}
|
||||
};
|
||||
Misc::forEachUnique(refs.rbegin(), refs.rend(), equalByRefNum, incrementRefCount);
|
||||
}
|
||||
|
||||
int ESMStore::getRefCount(const std::string& id) const
|
||||
|
|
36
components/misc/algorithm.hpp
Normal file
36
components/misc/algorithm.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef OPENMW_COMPONENTS_MISC_ALGORITHM_H
|
||||
#define OPENMW_COMPONENTS_MISC_ALGORITHM_H
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
template <typename Iterator, typename BinaryPredicate, typename Function>
|
||||
inline Iterator forEachUnique(Iterator begin, Iterator end, BinaryPredicate predicate, Function function)
|
||||
{
|
||||
static_assert(
|
||||
std::is_base_of_v<
|
||||
std::forward_iterator_tag,
|
||||
typename std::iterator_traits<Iterator>::iterator_category
|
||||
>
|
||||
);
|
||||
if (begin == end)
|
||||
return begin;
|
||||
function(*begin);
|
||||
auto last = begin;
|
||||
++begin;
|
||||
while (begin != end)
|
||||
{
|
||||
if (!predicate(*begin, *last))
|
||||
{
|
||||
function(*begin);
|
||||
last = begin;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue