1 /* 2 * Copyright (C) 2022 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 SRC_TRACE_PROCESSOR_DB_COLUMN_STORAGE_H_ 18 #define SRC_TRACE_PROCESSOR_DB_COLUMN_STORAGE_H_ 19 20 #include <cstddef> 21 #include <cstdint> 22 #include <optional> 23 #include <vector> 24 25 #include "perfetto/base/compiler.h" 26 #include "perfetto/base/logging.h" 27 #include "perfetto/public/compiler.h" 28 #include "src/trace_processor/containers/bit_vector.h" 29 30 namespace perfetto::trace_processor { 31 32 // Base class for allowing type erasure when defining plug-in implementations 33 // of backing storage for columns. 34 class ColumnStorageBase { 35 public: 36 ColumnStorageBase() = default; 37 virtual ~ColumnStorageBase(); 38 39 ColumnStorageBase(const ColumnStorageBase&) = delete; 40 ColumnStorageBase& operator=(const ColumnStorageBase&) = delete; 41 42 ColumnStorageBase(ColumnStorageBase&&) = default; 43 ColumnStorageBase& operator=(ColumnStorageBase&&) noexcept = default; 44 45 virtual const void* data() const = 0; 46 virtual const BitVector* bv() const = 0; 47 virtual uint32_t size() const = 0; 48 virtual uint32_t non_null_size() const = 0; 49 }; 50 51 // Class used for implementing storage for non-null columns. 52 template <typename T> 53 class ColumnStorage final : public ColumnStorageBase { 54 public: 55 ColumnStorage() = default; 56 57 explicit ColumnStorage(const ColumnStorage&) = delete; 58 ColumnStorage& operator=(const ColumnStorage&) = delete; 59 60 ColumnStorage(ColumnStorage&&) = default; 61 ColumnStorage& operator=(ColumnStorage&&) noexcept = default; 62 Get(uint32_t idx)63 T Get(uint32_t idx) const { return vector_[idx]; } Append(T val)64 void Append(T val) { vector_.emplace_back(val); } Set(uint32_t idx,T val)65 void Set(uint32_t idx, T val) { vector_[idx] = val; } ShrinkToFit()66 PERFETTO_NO_INLINE void ShrinkToFit() { vector_.shrink_to_fit(); } vector()67 const std::vector<T>& vector() const { return vector_; } 68 data()69 const void* data() const final { return vector_.data(); } bv()70 const BitVector* bv() const final { return nullptr; } size()71 uint32_t size() const final { return static_cast<uint32_t>(vector_.size()); } non_null_size()72 uint32_t non_null_size() const final { return size(); } 73 74 template <bool IsDense> Create()75 static ColumnStorage<T> Create() { 76 static_assert(!IsDense, "Invalid for non-null storage to be dense."); 77 return ColumnStorage<T>(); 78 } 79 80 // Create non-null storage from nullable storage without nulls. CreateFromAssertNonNull(ColumnStorage<std::optional<T>> null_storage)81 static ColumnStorage<T> CreateFromAssertNonNull( 82 ColumnStorage<std::optional<T>> null_storage) { 83 PERFETTO_CHECK(null_storage.size() == null_storage.non_null_size()); 84 ColumnStorage<T> x; 85 x.vector_ = std::move(null_storage).non_null_vector(); 86 return x; 87 } 88 89 private: 90 std::vector<T> vector_; 91 }; 92 93 // Class used for implementing storage for nullable columns. 94 template <typename T> 95 class ColumnStorage<std::optional<T>> final : public ColumnStorageBase { 96 public: 97 ColumnStorage() = default; 98 99 explicit ColumnStorage(const ColumnStorage&) = delete; 100 ColumnStorage& operator=(const ColumnStorage&) = delete; 101 102 ColumnStorage(ColumnStorage&&) = default; 103 ColumnStorage& operator=(ColumnStorage&&) noexcept = default; 104 Get(uint32_t idx)105 std::optional<T> Get(uint32_t idx) const { 106 bool contains = valid_.IsSet(idx); 107 if (mode_ == Mode::kDense) { 108 return contains ? std::make_optional(data_[idx]) : std::nullopt; 109 } 110 return contains ? std::make_optional(data_[valid_.CountSetBits(idx)]) 111 : std::nullopt; 112 } Append(T val)113 void Append(T val) { 114 data_.emplace_back(val); 115 valid_.AppendTrue(); 116 } Append(std::optional<T> val)117 void Append(std::optional<T> val) { 118 if (val) { 119 Append(*val); 120 } else { 121 AppendNull(); 122 } 123 } Set(uint32_t idx,T val)124 void Set(uint32_t idx, T val) { 125 if (mode_ == Mode::kDense) { 126 valid_.Set(idx); 127 data_[idx] = val; 128 } else { 129 // Generally, we will be setting a null row to non-null so optimize for 130 // that path. 131 uint32_t row = valid_.CountSetBits(idx); 132 bool was_set = valid_.Set(idx); 133 if (PERFETTO_UNLIKELY(was_set)) { 134 data_[row] = val; 135 } else { 136 data_.insert(data_.begin() + static_cast<ptrdiff_t>(row), val); 137 } 138 } 139 } IsDense()140 bool IsDense() const { return mode_ == Mode::kDense; } ShrinkToFit()141 PERFETTO_NO_INLINE void ShrinkToFit() { 142 data_.shrink_to_fit(); 143 valid_.ShrinkToFit(); 144 } 145 // For dense columns the size of the vector is equal to size of the bit 146 // vector. For sparse it's equal to count set bits of the bit vector. non_null_vector()147 const std::vector<T>& non_null_vector() const& { return data_; } non_null_bit_vector()148 const BitVector& non_null_bit_vector() const { return valid_; } 149 data()150 const void* data() const final { return non_null_vector().data(); } bv()151 const BitVector* bv() const final { return &non_null_bit_vector(); } size()152 uint32_t size() const final { return valid_.size(); } non_null_size()153 uint32_t non_null_size() const final { 154 return static_cast<uint32_t>(non_null_vector().size()); 155 } 156 157 template <bool IsDense> Create()158 static ColumnStorage<std::optional<T>> Create() { 159 return IsDense ? ColumnStorage<std::optional<T>>(Mode::kDense) 160 : ColumnStorage<std::optional<T>>(Mode::kSparse); 161 } 162 non_null_vector()163 std::vector<T> non_null_vector() && { return std::move(data_); } 164 165 private: 166 enum class Mode { 167 // Sparse mode is the default mode and ensures that nulls are stored using 168 // only 169 // a single bit (at the cost of making setting null entries to non-null 170 // O(n)). 171 kSparse, 172 173 // Dense mode forces the reservation of space for null entries which 174 // increases 175 // memory usage but allows for O(1) set operations. 176 kDense, 177 }; 178 ColumnStorage(Mode mode)179 explicit ColumnStorage(Mode mode) : mode_(mode) {} 180 AppendNull()181 void AppendNull() { 182 if (mode_ == Mode::kDense) { 183 data_.emplace_back(); 184 } 185 valid_.AppendFalse(); 186 } 187 188 Mode mode_ = Mode::kSparse; 189 std::vector<T> data_; 190 BitVector valid_; 191 }; 192 193 } // namespace perfetto::trace_processor 194 195 #endif // SRC_TRACE_PROCESSOR_DB_COLUMN_STORAGE_H_ 196