Skip to content

Commit 89b2303

Browse files
JakeHillionfacebook-github-bot
authored andcommitted
tbv2: remove unnecessary copy in Element (#457)
Summary: tbv2: remove unnecessary copy in Element `IntrospectionResult::const_iterator` iterates through the `Element`s in an `IntrospectionResult`. `Element` currently copies the `type_path` which is a `std::vector<string_view>` every time the iterator is incremented. This is unnecessary as the data in the vector only changes slightly between iterations. This change changes the `type_path` field in `Element` to a `std::span<const std::string_view>`. Doing this previously caused SEGVs because of the iterator's potential to be copied. To make it possible we do two things: 1. Make all copies explicit using a clone interface as in `ContainerInfo`. This prevents accidental copies of an expensive structure. 2. After calling the copy constructor in `clone()` update the `span` in `next_` to point at the newly copied structure. Moves are fine because the `span` points at the allocation of the `vector`, not the vector itself. Test Plan: - CI - `FILTER='OilIntegration.*' make test` - Ran `OilgenIntegration.std_vector_vector_int_some` which SEGVd with the `span` change before and now doesn't. This now passes cleanly with ASAN enabled on the target, though isn't available in `main` (only works on my machine). Differential Revision: D53472595 Pulled By: JakeHillion
1 parent f076b34 commit 89b2303

5 files changed

+34
-9
lines changed

include/oi/IntrospectionResult-inl.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ inline IntrospectionResult::const_iterator IntrospectionResult::begin() const {
4545
return cbegin();
4646
}
4747
inline IntrospectionResult::const_iterator IntrospectionResult::cbegin() const {
48-
return ++const_iterator{buf_.cbegin(), inst_};
48+
auto it = const_iterator{buf_.cbegin(), inst_};
49+
++it;
50+
return it;
4951
}
5052
inline IntrospectionResult::const_iterator IntrospectionResult::end() const {
5153
return cend();
@@ -70,6 +72,18 @@ inline const result::Element* IntrospectionResult::const_iterator::operator->()
7072
return &*next_;
7173
}
7274

75+
inline IntrospectionResult::const_iterator
76+
IntrospectionResult::const_iterator::clone() const {
77+
auto ret{*this};
78+
79+
// Fix the self referential type_path_ field in next_
80+
if (ret.next_.has_value()) {
81+
ret.next_->type_path = ret.type_path_;
82+
}
83+
84+
return ret;
85+
}
86+
7387
inline bool IntrospectionResult::const_iterator::operator==(
7488
const IntrospectionResult::const_iterator& that) const {
7589
// Case 1: The next data to read differs, thus the iterators are different.

include/oi/IntrospectionResult.h

+10
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ class IntrospectionResult {
4343
const_iterator& operator++();
4444
const_iterator operator++(int);
4545

46+
private:
47+
const_iterator(const const_iterator&) = default;
48+
const_iterator& operator=(const const_iterator& other) = default;
49+
50+
public:
51+
const_iterator(const_iterator&&) = default;
52+
const_iterator& operator=(const_iterator&&) = default;
53+
// Explicit interface for copying
54+
const_iterator clone() const;
55+
4656
private:
4757
using stack_t =
4858
std::stack<exporters::inst::Inst, std::vector<exporters::inst::Inst>>;

include/oi/result/Element.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ struct Element {
4141
};
4242

4343
std::string_view name;
44-
std::vector<std::string_view>
45-
type_path; // TODO: should be span<const std::string_view>
44+
std::span<const std::string_view> type_path;
4645
std::span<const std::string_view> type_names;
4746
size_t static_size;
4847
size_t exclusive_size;

include/oi/result/SizedResult-inl.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ SizedResult<Res>::SizedResult(Res res) : res_{std::move(res)} {
3535

3636
template <typename Res>
3737
typename SizedResult<Res>::const_iterator SizedResult<Res>::begin() const {
38-
return ++const_iterator{res_.begin(), res_.end()};
38+
const_iterator it{res_.begin(), res_.end()};
39+
++it;
40+
return it;
3941
}
4042
template <typename Res>
4143
typename SizedResult<Res>::const_iterator SizedResult<Res>::end() const {
@@ -44,7 +46,7 @@ typename SizedResult<Res>::const_iterator SizedResult<Res>::end() const {
4446

4547
template <typename Res>
4648
SizedResult<Res>::const_iterator::const_iterator(It it, const It& end)
47-
: data_{it} {
49+
: data_{it.clone()} {
4850
struct StackEntry {
4951
size_t index;
5052
size_t depth;
@@ -75,7 +77,8 @@ SizedResult<Res>::const_iterator::const_iterator(It it, const It& end)
7577
}
7678

7779
template <typename Res>
78-
SizedResult<Res>::const_iterator::const_iterator(It end) : data_{end} {
80+
SizedResult<Res>::const_iterator::const_iterator(It end)
81+
: data_{std::move(end)} {
7982
}
8083

8184
template <typename Res>

oi/IntrospectionResult.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ IntrospectionResult::const_iterator::operator++() {
7070
if constexpr (std::is_same_v<T, exporters::inst::Field>) {
7171
type_path_.emplace_back(ty.name);
7272
stack_.emplace(exporters::inst::PopTypePath{});
73-
next_ = result::Element{
73+
next_.emplace(result::Element{
7474
.name = ty.name,
7575
.type_path = type_path_,
7676
.type_names = ty.type_names,
@@ -79,7 +79,7 @@ IntrospectionResult::const_iterator::operator++() {
7979
.container_stats = std::nullopt,
8080
.is_set_stats = std::nullopt,
8181
.is_primitive = ty.is_primitive,
82-
};
82+
});
8383

8484
for (const auto& [dy, handler] : ty.processors) {
8585
auto parsed = exporters::ParsedData::parse(data_, dy);
@@ -94,7 +94,6 @@ IntrospectionResult::const_iterator::operator++() {
9494
.second;
9595

9696
type_path_.back() = new_name_ref;
97-
next_->type_path.back() = new_name_ref;
9897
next_->name = new_name_ref;
9998
}
10099

0 commit comments

Comments
 (0)