object-introspection/include/oi/result/SizedResult-inl.h
Jake Hillion 71e734b120 tbv2: calculate total memory footprint
Add the option to calculate total size (inclusive size) by wrapping the
existing iterator. This change provides a new iterator, `SizedIterator`, which
wraps an existing iterator and adds a new field `size` to the output element.

This is achieved with a two pass algorithm on the existing iterator:
1. Gather metadata for each element. This includes the total size up until that
   element and the range of elements that should be included in the size.
2. Return the result from the underlying iterator with the additional
   field.

This algorithm is `O(N)` time on the number of elements in the iterator and
`O(N)` time, storing 16 bytes per element. This isn't super expensive but is a
lot more than the current algorithm which requires close to constant space.
Because of this I've implemented it as a wrapper on the iterator rather than on
by default, though it is now on in every one of our integration test cases.

Test plan:
- Added to the integration tests for full coverage.
2024-01-04 09:21:35 +00:00

145 lines
3.8 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(INCLUDED_OI_RESULT_SIZED_ELEMENT_INL_H) || \
!defined(INCLUDED_OI_RESULT_SIZED_ELEMENT_H)
static_assert(false,
"SizedResult-inl.h provides inline declarations for "
"SizedResult.h and should only "
"be included by SizedResult.h");
#endif
#define INCLUDED_OI_RESULT_SIZED_ELEMENT_INL_H 1
#include <utility>
#include <vector>
#include "SizedResult.h"
namespace oi::result {
template <typename Res>
SizedResult<Res>::SizedResult(Res res) : res_{std::move(res)} {
}
template <typename Res>
typename SizedResult<Res>::const_iterator SizedResult<Res>::begin() const {
return ++const_iterator{res_.begin(), res_.end()};
}
template <typename Res>
typename SizedResult<Res>::const_iterator SizedResult<Res>::end() const {
return res_.end();
}
template <typename Res>
SizedResult<Res>::const_iterator::const_iterator(It it, const It& end)
: data_{it} {
struct StackEntry {
size_t index;
size_t depth;
};
std::vector<StackEntry> stack;
size_t size = 0;
size_t count = -1;
for (; it != end; ++it) {
++count;
auto depth = it->type_path.size();
while (!stack.empty() && stack.back().depth >= depth) {
auto i = stack.back().index;
stack.pop_back();
helpers_[i].last_child = count - 1;
}
size += it->exclusive_size;
helpers_.emplace_back(SizeHelper{.size = size});
stack.emplace_back(StackEntry{.index = count, .depth = depth});
}
while (!stack.empty()) {
auto i = stack.back().index;
stack.pop_back();
helpers_[i].last_child = count;
}
}
template <typename Res>
SizedResult<Res>::const_iterator::const_iterator(It end) : data_{end} {
}
template <typename Res>
typename SizedResult<Res>::const_iterator
SizedResult<Res>::const_iterator::operator++(int) {
auto old = *this;
operator++();
return old;
}
template <typename Res>
typename SizedResult<Res>::const_iterator&
SizedResult<Res>::const_iterator::operator++() {
// The below iterator is already pointing at the first element while this
// iterator is not. Skip incrementing it the first time around.
if (count_ != 0)
++data_;
if (count_ == helpers_.size()) {
next_.reset();
return *this;
}
size_t size = helpers_[helpers_[count_].last_child].size;
if (count_ != 0)
size -= helpers_[count_ - 1].size;
next_.emplace(*data_, size);
++count_;
return *this;
}
template <typename Res>
const typename SizedResult<Res>::Element&
SizedResult<Res>::const_iterator::operator*() const {
return *next_;
}
template <typename Res>
const typename SizedResult<Res>::Element*
SizedResult<Res>::const_iterator::operator->() const {
return &*next_;
}
template <typename Res>
bool SizedResult<Res>::const_iterator::operator==(
const SizedResult<Res>::const_iterator& that) const {
return this->data_ == that.data_;
}
template <typename Res>
bool SizedResult<Res>::const_iterator::operator!=(
const SizedResult<Res>::const_iterator& that) const {
return !(*this == that);
}
template <typename El>
SizedElement<El>::SizedElement(const El& el, size_t size_)
: El{el}, size{size_} {
}
template <typename El>
const El& SizedElement<El>::inner() const {
return static_cast<const El&>(*this);
}
} // namespace oi::result