/src/perfetto/include/perfetto/base/flat_set.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019 The Android Open Source Project |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #ifndef INCLUDE_PERFETTO_BASE_FLAT_SET_H_ |
18 | | #define INCLUDE_PERFETTO_BASE_FLAT_SET_H_ |
19 | | |
20 | | #include <stddef.h> |
21 | | |
22 | | #include <algorithm> |
23 | | #include <utility> |
24 | | #include <vector> |
25 | | |
26 | | // A vector-based set::set-like container. |
27 | | // It's more cache friendly than std::*set and performs for cases where: |
28 | | // 1. A high number of dupes is expected (e.g. pid tracking in ftrace). |
29 | | // 2. The working set is small (hundreds of elements). |
30 | | |
31 | | // Performance characteristics (for uniformly random insertion order): |
32 | | // - For smaller insertions (up to ~500), it outperforms both std::set<int> and |
33 | | // std::unordered_set<int> by ~3x. |
34 | | // - Up until 4k insertions, it is always faster than std::set<int>. |
35 | | // - unordered_set<int> is faster with more than 2k insertions. |
36 | | // - unordered_set, however, it's less memory efficient and has more caveats |
37 | | // (see chromium's base/containers/README.md). |
38 | | // |
39 | | // See flat_set_benchmark.cc and the charts in go/perfetto-int-set-benchmark. |
40 | | |
41 | | namespace perfetto { |
42 | | namespace base { |
43 | | |
44 | | template <typename T> |
45 | | class FlatSet { |
46 | | public: |
47 | | using value_type = T; |
48 | | using const_pointer = const T*; |
49 | | using iterator = typename std::vector<T>::iterator; |
50 | | using const_iterator = typename std::vector<T>::const_iterator; |
51 | | |
52 | 9.22k | FlatSet() = default; perfetto::base::FlatSet<std::__1::pair<unsigned long, unsigned long> >::FlatSet() Line | Count | Source | 52 | 1.31k | FlatSet() = default; |
perfetto::base::FlatSet<int>::FlatSet() Line | Count | Source | 52 | 2.63k | FlatSet() = default; |
perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::FlatSet() Line | Count | Source | 52 | 1.31k | FlatSet() = default; |
perfetto::base::FlatSet<std::__1::pair<int, unsigned long> >::FlatSet() Line | Count | Source | 52 | 1.31k | FlatSet() = default; |
perfetto::base::FlatSet<long>::FlatSet() Line | Count | Source | 52 | 1.31k | FlatSet() = default; |
perfetto::base::FlatSet<perfetto::protos::pbzero::FtraceParseStatus>::FlatSet() Line | Count | Source | 52 | 1.31k | FlatSet() = default; |
Unexecuted instantiation: perfetto::base::FlatSet<perfetto::GroupAndName>::FlatSet() perfetto::base::FlatSet<perfetto::PrintkEntry>::FlatSet() Line | Count | Source | 52 | 1 | FlatSet() = default; |
Unexecuted instantiation: perfetto::base::FlatSet<perfetto::ProcessStatsDataSource::SeenPid>::FlatSet() Unexecuted instantiation: perfetto::base::FlatSet<unsigned long>::FlatSet() Unexecuted instantiation: perfetto::base::FlatSet<perfetto::trace_processor::tables::StackProfileCallsiteTable::Id>::FlatSet() |
53 | | |
54 | | // Mainly for tests. Deliberately not marked as "explicit". |
55 | | FlatSet(std::initializer_list<T> initial) : entries_(initial) { |
56 | | std::sort(entries_.begin(), entries_.end()); |
57 | | entries_.erase(std::unique(entries_.begin(), entries_.end()), |
58 | | entries_.end()); |
59 | | } |
60 | | |
61 | 0 | const_iterator find(T value) const { |
62 | 0 | auto entries_end = entries_.end(); |
63 | 0 | auto it = std::lower_bound(entries_.begin(), entries_end, value); |
64 | 0 | return (it != entries_end && *it == value) ? it : entries_end; |
65 | 0 | } Unexecuted instantiation: perfetto::base::FlatSet<perfetto::PrintkEntry>::find(perfetto::PrintkEntry) const Unexecuted instantiation: perfetto::base::FlatSet<long>::find(long) const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::ProcessStatsDataSource::SeenPid>::find(perfetto::ProcessStatsDataSource::SeenPid) const Unexecuted instantiation: perfetto::base::FlatSet<unsigned long>::find(unsigned long) const |
66 | | |
67 | 0 | size_t count(T value) const { return find(value) == entries_.end() ? 0 : 1; }Unexecuted instantiation: perfetto::base::FlatSet<long>::count(long) const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::ProcessStatsDataSource::SeenPid>::count(perfetto::ProcessStatsDataSource::SeenPid) const Unexecuted instantiation: perfetto::base::FlatSet<unsigned long>::count(unsigned long) const |
68 | | |
69 | 12.5k | std::pair<iterator, bool> insert(T value) { |
70 | 12.5k | auto entries_end = entries_.end(); |
71 | 12.5k | auto it = std::lower_bound(entries_.begin(), entries_end, value); |
72 | 12.5k | if (it != entries_end && *it == value) |
73 | 5.44k | return std::make_pair(it, false); |
74 | | // If the value is not found |it| is either end() or the next item strictly |
75 | | // greater than |value|. In both cases we want to insert just before that. |
76 | 7.14k | it = entries_.insert(it, std::move(value)); |
77 | 7.14k | return std::make_pair(it, true); |
78 | 12.5k | } Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<unsigned long, unsigned long> >::insert(std::__1::pair<unsigned long, unsigned long>) perfetto::base::FlatSet<int>::insert(int) Line | Count | Source | 69 | 11.4k | std::pair<iterator, bool> insert(T value) { | 70 | 11.4k | auto entries_end = entries_.end(); | 71 | 11.4k | auto it = std::lower_bound(entries_.begin(), entries_end, value); | 72 | 11.4k | if (it != entries_end && *it == value) | 73 | 5.44k | return std::make_pair(it, false); | 74 | | // If the value is not found |it| is either end() or the next item strictly | 75 | | // greater than |value|. In both cases we want to insert just before that. | 76 | 6.00k | it = entries_.insert(it, std::move(value)); | 77 | 6.00k | return std::make_pair(it, true); | 78 | 11.4k | } |
Unexecuted instantiation: perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::insert(perfetto::FtraceMetadata::KernelAddr) perfetto::base::FlatSet<perfetto::PrintkEntry>::insert(perfetto::PrintkEntry) Line | Count | Source | 69 | 5 | std::pair<iterator, bool> insert(T value) { | 70 | 5 | auto entries_end = entries_.end(); | 71 | 5 | auto it = std::lower_bound(entries_.begin(), entries_end, value); | 72 | 5 | if (it != entries_end && *it == value) | 73 | 1 | return std::make_pair(it, false); | 74 | | // If the value is not found |it| is either end() or the next item strictly | 75 | | // greater than |value|. In both cases we want to insert just before that. | 76 | 4 | it = entries_.insert(it, std::move(value)); | 77 | 4 | return std::make_pair(it, true); | 78 | 5 | } |
perfetto::base::FlatSet<perfetto::protos::pbzero::FtraceParseStatus>::insert(perfetto::protos::pbzero::FtraceParseStatus) Line | Count | Source | 69 | 1.13k | std::pair<iterator, bool> insert(T value) { | 70 | 1.13k | auto entries_end = entries_.end(); | 71 | 1.13k | auto it = std::lower_bound(entries_.begin(), entries_end, value); | 72 | 1.13k | if (it != entries_end && *it == value) | 73 | 0 | return std::make_pair(it, false); | 74 | | // If the value is not found |it| is either end() or the next item strictly | 75 | | // greater than |value|. In both cases we want to insert just before that. | 76 | 1.13k | it = entries_.insert(it, std::move(value)); | 77 | 1.13k | return std::make_pair(it, true); | 78 | 1.13k | } |
Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<int, unsigned long> >::insert(std::__1::pair<int, unsigned long>) Unexecuted instantiation: perfetto::base::FlatSet<long>::insert(long) Unexecuted instantiation: perfetto::base::FlatSet<perfetto::GroupAndName>::insert(perfetto::GroupAndName) Unexecuted instantiation: perfetto::base::FlatSet<perfetto::ProcessStatsDataSource::SeenPid>::insert(perfetto::ProcessStatsDataSource::SeenPid) Unexecuted instantiation: perfetto::base::FlatSet<unsigned long>::insert(unsigned long) Unexecuted instantiation: perfetto::base::FlatSet<perfetto::trace_processor::tables::StackProfileCallsiteTable::Id>::insert(perfetto::trace_processor::tables::StackProfileCallsiteTable::Id) |
79 | | |
80 | 0 | size_t erase(T value) { |
81 | 0 | auto it = find(value); |
82 | 0 | if (it == entries_.end()) |
83 | 0 | return 0; |
84 | 0 | entries_.erase(it); |
85 | 0 | return 1; |
86 | 0 | } |
87 | | |
88 | 0 | void clear() { entries_.clear(); }Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<unsigned long, unsigned long> >::clear() Unexecuted instantiation: perfetto::base::FlatSet<int>::clear() Unexecuted instantiation: perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::clear() Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<int, unsigned long> >::clear() Unexecuted instantiation: perfetto::base::FlatSet<perfetto::GroupAndName>::clear() Unexecuted instantiation: perfetto::base::FlatSet<perfetto::ProcessStatsDataSource::SeenPid>::clear() |
89 | | |
90 | 0 | bool empty() const { return entries_.empty(); }Unexecuted instantiation: perfetto::base::FlatSet<perfetto::PrintkEntry>::empty() const Unexecuted instantiation: perfetto::base::FlatSet<int>::empty() const Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<int, unsigned long> >::empty() const |
91 | 3.95k | void reserve(size_t n) { entries_.reserve(n); }perfetto::base::FlatSet<int>::reserve(unsigned long) Line | Count | Source | 91 | 2.63k | void reserve(size_t n) { entries_.reserve(n); } |
perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::reserve(unsigned long) Line | Count | Source | 91 | 1.31k | void reserve(size_t n) { entries_.reserve(n); } |
|
92 | 0 | size_t size() const { return entries_.size(); }Unexecuted instantiation: perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::size() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::PrintkEntry>::size() const |
93 | 0 | const_iterator begin() const { return entries_.begin(); }Unexecuted instantiation: perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::begin() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::GroupAndName>::begin() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::protos::pbzero::FtraceParseStatus>::begin() const Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<unsigned long, unsigned long> >::begin() const Unexecuted instantiation: perfetto::base::FlatSet<int>::begin() const Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<int, unsigned long> >::begin() const |
94 | 0 | const_iterator end() const { return entries_.end(); }Unexecuted instantiation: perfetto::base::FlatSet<perfetto::PrintkEntry>::end() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::FtraceMetadata::KernelAddr>::end() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::GroupAndName>::end() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::protos::pbzero::FtraceParseStatus>::end() const Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<unsigned long, unsigned long> >::end() const Unexecuted instantiation: perfetto::base::FlatSet<int>::end() const Unexecuted instantiation: perfetto::base::FlatSet<std::__1::pair<int, unsigned long> >::end() const Unexecuted instantiation: perfetto::base::FlatSet<perfetto::ProcessStatsDataSource::SeenPid>::end() const |
95 | | |
96 | | private: |
97 | | std::vector<T> entries_; |
98 | | }; |
99 | | |
100 | | } // namespace base |
101 | | } // namespace perfetto |
102 | | |
103 | | #endif // INCLUDE_PERFETTO_BASE_FLAT_SET_H_ |