From fd4c5381802345c15ec252599b2f4cf70dd87272 Mon Sep 17 00:00:00 2001 From: Martchus Date: Wed, 27 Jan 2021 21:24:26 +0100 Subject: [PATCH] Improve performance of Matroska writer Especially when dealing with big files the performance is quite bad. This change speeds it up a little by using an unordered map to find the elements for certain offsets instead of using linear lookup. According to callgrind the modified functions where one with the biggest own cost. The function updateRelativeOffsets() was actually the 2nd costly function just below __memcpy_avx_unaligned_erms. Now TagParser::EbmlElement::internalParse(TagParser::Diagnostics&) is quite high as well as standard IO stream functions. --- matroska/matroskacues.cpp | 40 ++++++++++++++++++++++++++++----------- matroska/matroskacues.h | 12 ++++++++++++ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/matroska/matroskacues.cpp b/matroska/matroskacues.cpp index 7e62555..70fa57e 100644 --- a/matroska/matroskacues.cpp +++ b/matroska/matroskacues.cpp @@ -96,11 +96,13 @@ void MatroskaCuePositionUpdater::parse(EbmlElement *cuesElement, Diagnostics &di pos = (cueClusterPositionElement = cueTrackPositionsChild)->readUInteger(); cueTrackPositionsElementSize += 2 + EbmlElement::calculateUIntegerLength(pos); m_offsets.emplace(cueTrackPositionsChild, pos); + m_cueElementByOriginalOffset.emplace(pos, cueTrackPositionsChild); break; case MatroskaIds::CueCodecState: statePos = cueTrackPositionsChild->readUInteger(); cueTrackPositionsElementSize += 2 + EbmlElement::calculateUIntegerLength(statePos); m_offsets.emplace(cueTrackPositionsChild, statePos); + m_cueElementByOriginalOffset.emplace(statePos, cueTrackPositionsChild); break; case MatroskaIds::CueReference: cueReferenceElementSize = 0; @@ -122,6 +124,7 @@ void MatroskaCuePositionUpdater::parse(EbmlElement *cuesElement, Diagnostics &di statePos = cueReferenceChild->readUInteger(); cueReferenceElementSize += 2 + EbmlElement::calculateUIntegerLength(statePos); m_offsets.emplace(cueReferenceChild, statePos); + m_cueElementByOriginalOffset.emplace(statePos, cueReferenceChild); break; default: diag.emplace_back(DiagLevel::Warning, @@ -143,6 +146,8 @@ void MatroskaCuePositionUpdater::parse(EbmlElement *cuesElement, Diagnostics &di } else if (cueRelativePositionElement) { cueTrackPositionsElementSize += 2 + EbmlElement::calculateUIntegerLength(relPos); m_relativeOffsets.emplace(piecewise_construct, forward_as_tuple(cueRelativePositionElement), forward_as_tuple(pos, relPos)); + m_cueRelativePositionElementByOriginalOffsets.emplace( + piecewise_construct, forward_as_tuple(pos, relPos), forward_as_tuple(cueRelativePositionElement)); } cuePointElementSize += 1 + EbmlElement::calculateSizeDenotationLength(cueTrackPositionsElementSize) + cueTrackPositionsElementSize; @@ -173,12 +178,19 @@ bool MatroskaCuePositionUpdater::updateOffsets(std::uint64_t originalOffset, std { auto updated = false; const auto newOffsetLength = static_cast(EbmlElement::calculateUIntegerLength(newOffset)); - for (auto &offset : m_offsets) { - if (offset.second.initialValue() == originalOffset && offset.second.currentValue() != newOffset) { - updated = updateSize(offset.first->parent(), newOffsetLength - - static_cast(EbmlElement::calculateUIntegerLength(offset.second.currentValue()))) + for (auto cueElementRange = m_cueElementByOriginalOffset.equal_range(originalOffset); cueElementRange.first != cueElementRange.second; + ++cueElementRange.first) { + auto *const cueElement = cueElementRange.first->second; + const auto offsetIterator = m_offsets.find(cueElement); + if (offsetIterator == m_offsets.end()) { + continue; + } + auto &offset = offsetIterator->second; + if (offset.currentValue() != newOffset) { + updated + = updateSize(cueElement->parent(), newOffsetLength - static_cast(EbmlElement::calculateUIntegerLength(offset.currentValue()))) || updated; - offset.second.update(newOffset); + offset.update(newOffset); } } return updated; @@ -193,13 +205,19 @@ bool MatroskaCuePositionUpdater::updateRelativeOffsets( { auto updated = false; const auto newRelativeOffsetLength = static_cast(EbmlElement::calculateUIntegerLength(newRelativeOffset)); - for (auto &offset : m_relativeOffsets) { - if (offset.second.referenceOffset() == referenceOffset && offset.second.initialValue() == originalRelativeOffset - && offset.second.currentValue() != newRelativeOffset) { - updated = updateSize(offset.first->parent(), newRelativeOffsetLength - - static_cast(EbmlElement::calculateUIntegerLength(offset.second.currentValue()))) + for (auto cueElementRange = m_cueRelativePositionElementByOriginalOffsets.equal_range(std::make_pair(referenceOffset, originalRelativeOffset)); + cueElementRange.first != cueElementRange.second; ++cueElementRange.first) { + auto *const cueRelativePositionElement = cueElementRange.first->second; + const auto offsetIterator = m_relativeOffsets.find(cueRelativePositionElement); + if (offsetIterator == m_relativeOffsets.end()) { + continue; + } + auto &offset = offsetIterator->second; + if (offset.currentValue() != newRelativeOffset) { + updated = updateSize(cueRelativePositionElement->parent(), + newRelativeOffsetLength - static_cast(EbmlElement::calculateUIntegerLength(offset.currentValue()))) || updated; - offset.second.update(newRelativeOffset); + offset.update(newRelativeOffset); } } return updated; diff --git a/matroska/matroskacues.h b/matroska/matroskacues.h index acbcf46..1e08f69 100644 --- a/matroska/matroskacues.h +++ b/matroska/matroskacues.h @@ -75,11 +75,23 @@ public: void clear(); private: + struct PairHash { + template inline std::size_t operator()(const std::pair &pair) const + { + std::size_t seed = 0; + seed ^= std::hash()(pair.first) + 0x9e3779b9; + seed ^= std::hash()(pair.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; + bool updateSize(EbmlElement *element, int shift); EbmlElement *m_cuesElement; std::unordered_map m_offsets; + std::unordered_multimap m_cueElementByOriginalOffset; std::unordered_map m_relativeOffsets; + std::unordered_multimap, EbmlElement *, PairHash> m_cueRelativePositionElementByOriginalOffsets; std::unordered_map m_sizes; };