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 <algorithm> 21 #include <vector> 22 23 // A vector-based set::set-like container. 24 // It's more cache friendly than std::*set and performs for cases where: 25 // 1. A high number of dupes is expected (e.g. pid tracking in ftrace). 26 // 2. The working set is small (hundreds of elements). 27 28 // Performance characteristics (for uniformly random insertion order): 29 // - For smaller insertions (up to ~500), it outperforms both std::set<int> and 30 // std::unordered_set<int> by ~3x. 31 // - Up until 4k insertions, it is always faster than std::set<int>. 32 // - unordered_set<int> is faster with more than 2k insertions. 33 // - unordered_set, however, it's less memory efficient and has more caveats 34 // (see chromium's base/containers/README.md). 35 // 36 // See flat_set_benchmark.cc and the charts in go/perfetto-int-set-benchmark. 37 38 namespace perfetto { 39 namespace base { 40 41 template <typename T> 42 class FlatSet { 43 public: 44 using value_type = T; 45 using const_pointer = const T*; 46 using iterator = typename std::vector<T>::iterator; 47 using const_iterator = typename std::vector<T>::const_iterator; 48 49 FlatSet() = default; 50 51 // Mainly for tests. Deliberately not marked as "explicit". FlatSet(std::initializer_list<T> initial)52 FlatSet(std::initializer_list<T> initial) : entries_(initial) { 53 std::sort(entries_.begin(), entries_.end()); 54 entries_.erase(std::unique(entries_.begin(), entries_.end()), 55 entries_.end()); 56 } 57 find(T value)58 const_iterator find(T value) const { 59 auto entries_end = entries_.end(); 60 auto it = std::lower_bound(entries_.begin(), entries_end, value); 61 return (it != entries_end && *it == value) ? it : entries_end; 62 } 63 count(T value)64 size_t count(T value) const { return find(value) == entries_.end() ? 0 : 1; } 65 insert(T value)66 std::pair<iterator, bool> insert(T value) { 67 auto entries_end = entries_.end(); 68 auto it = std::lower_bound(entries_.begin(), entries_end, value); 69 if (it != entries_end && *it == value) 70 return std::make_pair(it, false); 71 // If the value is not found |it| is either end() or the next item strictly 72 // greater than |value|. In both cases we want to insert just before that. 73 it = entries_.insert(it, std::move(value)); 74 return std::make_pair(it, true); 75 } 76 erase(T value)77 size_t erase(T value) { 78 auto it = find(value); 79 if (it == entries_.end()) 80 return 0; 81 entries_.erase(it); 82 return 1; 83 } 84 clear()85 void clear() { entries_.clear(); } 86 empty()87 bool empty() const { return entries_.empty(); } reserve(size_t n)88 void reserve(size_t n) { entries_.reserve(n); } size()89 size_t size() const { return entries_.size(); } begin()90 const_iterator begin() const { return entries_.begin(); } end()91 const_iterator end() const { return entries_.end(); } 92 93 private: 94 std::vector<T> entries_; 95 }; 96 97 } // namespace base 98 } // namespace perfetto 99 100 #endif // INCLUDE_PERFETTO_BASE_FLAT_SET_H_ 101