Skip to content

Commit

Permalink
[#28] messages: replace _m_blocks_by_name with binary search
Browse files Browse the repository at this point in the history
The allocation of many thousands of small strings as keys of the
`std::unordered_map` used for looking up blocks by their name is not
very fast.

Using binary search instead improves parsing performance and decreases
memory usage while still keeping block lookup performance within an
acceptable range.
  • Loading branch information
lmichaelis committed Nov 6, 2022
1 parent b704f7e commit ebb48be
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
5 changes: 1 addition & 4 deletions include/phoenix/messages.hh
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,10 @@ namespace phoenix {
/// \brief Retrieves a message block by it's name.
/// \param name The name of the block to get
/// \return A pointer to the block or `nullptr` if the block was not found.
const message_block* block_by_name(const std::string& name) const;
const message_block* block_by_name(std::string_view name) const;

public:
/// \brief A list of all message blocks in the database.
std::vector<message_block> blocks {};

private:
std::unordered_map<std::string, std::uint32_t> _m_blocks_by_name {};
};
} // namespace phoenix
35 changes: 35 additions & 0 deletions include/phoenix/phoenix.hh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,41 @@ namespace phoenix {
/// \return ``true`` if both strings are equal when ignoring case.
bool iequals(std::string_view a, std::string_view b);

/// \brief Performs a binary search within the given iterator pair.
///
/// Taken directly from The example implementation of `lower_bound` found on cppreference
/// https://en.cppreference.com/w/cpp/algorithm/lower_bound#Possible_implementation
///
/// \tparam ForwardIt The type of iterator.
/// \tparam T The type of value to search for.
/// \tparam Compare The type of the comparator function.
/// \param first The begin iterator
/// \param last The end iterator
/// \param value The value to search for.
/// \param comp A function comparing a value from the iterator with the given search value.
/// \return An iterator pointing to the first matched value in the input range or \p last if no elements matched.
template <class ForwardIt, class T, class Compare>
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp) {
ForwardIt it;
typename std::iterator_traits<ForwardIt>::difference_type count, step;
count = std::distance(first, last);

while (count > 0) {
it = first;
step = count / 2;
std::advance(it, step);

if (comp(*it, value)) {
first = ++it;
count -= step + 1;
} else {
count = step;
}
}

return first;
}

/// \brief A basic datetime structure used by the *ZenGin*.
struct date {
/// \brief Parses a date from a buffer.
Expand Down
15 changes: 10 additions & 5 deletions source/messages.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ namespace phoenix {

itm.message.text = archive->read_string();
itm.message.name = archive->read_string();
msgs._m_blocks_by_name[itm.name] = msgs.blocks.size() - 1;

if (!archive->read_object_end()) {
// FIXME: in binary archives this might skip whole sections of the file due to faulty object
Expand All @@ -79,14 +78,20 @@ namespace phoenix {
PX_LOGW("messages: not fully parsed");
}

// Prepare blocks for binary search in block_by_name
std::sort(msgs.blocks.begin(), msgs.blocks.end(), [](const auto& a, const auto& b) { return a.name < b.name; });
return msgs;
}

const message_block* messages::block_by_name(const std::string& name) const {
if (auto it = _m_blocks_by_name.find(name); it != _m_blocks_by_name.end()) {
return &blocks[it->second];
const message_block* messages::block_by_name(std::string_view name) const {
auto result = phoenix::lower_bound(this->blocks.begin(), this->blocks.end(), name, [](const auto& it, auto n) {
return it.name < n;
});

if (result == this->blocks.end()) {
return nullptr;
}

return nullptr;
return &*result;
}
} // namespace phoenix

0 comments on commit ebb48be

Please sign in to comment.