xref: /aosp_15_r20/art/compiler/utils/dedupe_set-inl.h (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #ifndef ART_COMPILER_UTILS_DEDUPE_SET_INL_H_
18*795d594fSAndroid Build Coastguard Worker #define ART_COMPILER_UTILS_DEDUPE_SET_INL_H_
19*795d594fSAndroid Build Coastguard Worker 
20*795d594fSAndroid Build Coastguard Worker #include "dedupe_set.h"
21*795d594fSAndroid Build Coastguard Worker 
22*795d594fSAndroid Build Coastguard Worker #include <inttypes.h>
23*795d594fSAndroid Build Coastguard Worker 
24*795d594fSAndroid Build Coastguard Worker #include <algorithm>
25*795d594fSAndroid Build Coastguard Worker #include <unordered_map>
26*795d594fSAndroid Build Coastguard Worker 
27*795d594fSAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker #include "base/hash_set.h"
30*795d594fSAndroid Build Coastguard Worker #include "base/macros.h"
31*795d594fSAndroid Build Coastguard Worker #include "base/mutex.h"
32*795d594fSAndroid Build Coastguard Worker #include "base/stl_util.h"
33*795d594fSAndroid Build Coastguard Worker #include "base/time_utils.h"
34*795d594fSAndroid Build Coastguard Worker 
35*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
36*795d594fSAndroid Build Coastguard Worker 
37*795d594fSAndroid Build Coastguard Worker template <typename InKey,
38*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
39*795d594fSAndroid Build Coastguard Worker           typename Alloc,
40*795d594fSAndroid Build Coastguard Worker           typename HashType,
41*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
42*795d594fSAndroid Build Coastguard Worker           HashType kShard>
43*795d594fSAndroid Build Coastguard Worker struct DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Stats {
44*795d594fSAndroid Build Coastguard Worker   size_t collision_sum = 0u;
45*795d594fSAndroid Build Coastguard Worker   size_t collision_max = 0u;
46*795d594fSAndroid Build Coastguard Worker   size_t total_probe_distance = 0u;
47*795d594fSAndroid Build Coastguard Worker   size_t total_size = 0u;
48*795d594fSAndroid Build Coastguard Worker };
49*795d594fSAndroid Build Coastguard Worker 
50*795d594fSAndroid Build Coastguard Worker template <typename InKey,
51*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
52*795d594fSAndroid Build Coastguard Worker           typename Alloc,
53*795d594fSAndroid Build Coastguard Worker           typename HashType,
54*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
55*795d594fSAndroid Build Coastguard Worker           HashType kShard>
56*795d594fSAndroid Build Coastguard Worker class DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Shard {
57*795d594fSAndroid Build Coastguard Worker  public:
58*795d594fSAndroid Build Coastguard Worker   Shard(const Alloc& alloc, const std::string& lock_name)
59*795d594fSAndroid Build Coastguard Worker       : alloc_(alloc),
60*795d594fSAndroid Build Coastguard Worker         lock_name_(lock_name),
61*795d594fSAndroid Build Coastguard Worker         lock_(lock_name_.c_str()),
62*795d594fSAndroid Build Coastguard Worker         keys_() {
63*795d594fSAndroid Build Coastguard Worker   }
64*795d594fSAndroid Build Coastguard Worker 
65*795d594fSAndroid Build Coastguard Worker   ~Shard() {
66*795d594fSAndroid Build Coastguard Worker     for (const HashedKey<StoreKey>& key : keys_) {
67*795d594fSAndroid Build Coastguard Worker       DCHECK(key.Key() != nullptr);
68*795d594fSAndroid Build Coastguard Worker       alloc_.Destroy(key.Key());
69*795d594fSAndroid Build Coastguard Worker     }
70*795d594fSAndroid Build Coastguard Worker   }
71*795d594fSAndroid Build Coastguard Worker 
72*795d594fSAndroid Build Coastguard Worker   const StoreKey* Add(Thread* self, size_t hash, const InKey& in_key) REQUIRES(!lock_) {
73*795d594fSAndroid Build Coastguard Worker     MutexLock lock(self, lock_);
74*795d594fSAndroid Build Coastguard Worker     HashedKey<InKey> hashed_in_key(hash, &in_key);
75*795d594fSAndroid Build Coastguard Worker     auto it = keys_.find(hashed_in_key);
76*795d594fSAndroid Build Coastguard Worker     if (it != keys_.end()) {
77*795d594fSAndroid Build Coastguard Worker       DCHECK(it->Key() != nullptr);
78*795d594fSAndroid Build Coastguard Worker       return it->Key();
79*795d594fSAndroid Build Coastguard Worker     }
80*795d594fSAndroid Build Coastguard Worker     const StoreKey* store_key = alloc_.Copy(in_key);
81*795d594fSAndroid Build Coastguard Worker     keys_.insert(HashedKey<StoreKey> { hash, store_key });
82*795d594fSAndroid Build Coastguard Worker     return store_key;
83*795d594fSAndroid Build Coastguard Worker   }
84*795d594fSAndroid Build Coastguard Worker 
85*795d594fSAndroid Build Coastguard Worker   size_t Size(Thread* self) {
86*795d594fSAndroid Build Coastguard Worker     MutexLock lock(self, lock_);
87*795d594fSAndroid Build Coastguard Worker     return keys_.size();
88*795d594fSAndroid Build Coastguard Worker   }
89*795d594fSAndroid Build Coastguard Worker 
90*795d594fSAndroid Build Coastguard Worker   void UpdateStats(Thread* self, Stats* global_stats) REQUIRES(!lock_) {
91*795d594fSAndroid Build Coastguard Worker     // HashSet<> doesn't keep entries ordered by hash, so we actually allocate memory
92*795d594fSAndroid Build Coastguard Worker     // for bookkeeping while collecting the stats.
93*795d594fSAndroid Build Coastguard Worker     std::unordered_map<HashType, size_t> stats;
94*795d594fSAndroid Build Coastguard Worker     {
95*795d594fSAndroid Build Coastguard Worker       MutexLock lock(self, lock_);
96*795d594fSAndroid Build Coastguard Worker       // Note: The total_probe_distance will be updated with the current state.
97*795d594fSAndroid Build Coastguard Worker       // It may have been higher before a re-hash.
98*795d594fSAndroid Build Coastguard Worker       global_stats->total_probe_distance += keys_.TotalProbeDistance();
99*795d594fSAndroid Build Coastguard Worker       global_stats->total_size += keys_.size();
100*795d594fSAndroid Build Coastguard Worker       for (const HashedKey<StoreKey>& key : keys_) {
101*795d594fSAndroid Build Coastguard Worker         auto it = stats.find(key.Hash());
102*795d594fSAndroid Build Coastguard Worker         if (it == stats.end()) {
103*795d594fSAndroid Build Coastguard Worker           stats.insert({key.Hash(), 1u});
104*795d594fSAndroid Build Coastguard Worker         } else {
105*795d594fSAndroid Build Coastguard Worker           ++it->second;
106*795d594fSAndroid Build Coastguard Worker         }
107*795d594fSAndroid Build Coastguard Worker       }
108*795d594fSAndroid Build Coastguard Worker     }
109*795d594fSAndroid Build Coastguard Worker     for (const auto& entry : stats) {
110*795d594fSAndroid Build Coastguard Worker       size_t number_of_entries = entry.second;
111*795d594fSAndroid Build Coastguard Worker       if (number_of_entries > 1u) {
112*795d594fSAndroid Build Coastguard Worker         global_stats->collision_sum += number_of_entries - 1u;
113*795d594fSAndroid Build Coastguard Worker         global_stats->collision_max = std::max(global_stats->collision_max, number_of_entries);
114*795d594fSAndroid Build Coastguard Worker       }
115*795d594fSAndroid Build Coastguard Worker     }
116*795d594fSAndroid Build Coastguard Worker   }
117*795d594fSAndroid Build Coastguard Worker 
118*795d594fSAndroid Build Coastguard Worker  private:
119*795d594fSAndroid Build Coastguard Worker   template <typename T>
120*795d594fSAndroid Build Coastguard Worker   class HashedKey {
121*795d594fSAndroid Build Coastguard Worker    public:
122*795d594fSAndroid Build Coastguard Worker     HashedKey() : hash_(0u), key_(nullptr) { }
123*795d594fSAndroid Build Coastguard Worker     HashedKey(size_t hash, const T* key) : hash_(hash), key_(key) { }
124*795d594fSAndroid Build Coastguard Worker 
125*795d594fSAndroid Build Coastguard Worker     size_t Hash() const {
126*795d594fSAndroid Build Coastguard Worker       return hash_;
127*795d594fSAndroid Build Coastguard Worker     }
128*795d594fSAndroid Build Coastguard Worker 
129*795d594fSAndroid Build Coastguard Worker     const T* Key() const {
130*795d594fSAndroid Build Coastguard Worker       return key_;
131*795d594fSAndroid Build Coastguard Worker     }
132*795d594fSAndroid Build Coastguard Worker 
133*795d594fSAndroid Build Coastguard Worker     bool IsEmpty() const {
134*795d594fSAndroid Build Coastguard Worker       return Key() == nullptr;
135*795d594fSAndroid Build Coastguard Worker     }
136*795d594fSAndroid Build Coastguard Worker 
137*795d594fSAndroid Build Coastguard Worker     void MakeEmpty() {
138*795d594fSAndroid Build Coastguard Worker       key_ = nullptr;
139*795d594fSAndroid Build Coastguard Worker     }
140*795d594fSAndroid Build Coastguard Worker 
141*795d594fSAndroid Build Coastguard Worker    private:
142*795d594fSAndroid Build Coastguard Worker     size_t hash_;
143*795d594fSAndroid Build Coastguard Worker     const T* key_;
144*795d594fSAndroid Build Coastguard Worker   };
145*795d594fSAndroid Build Coastguard Worker 
146*795d594fSAndroid Build Coastguard Worker   class ShardEmptyFn {
147*795d594fSAndroid Build Coastguard Worker    public:
148*795d594fSAndroid Build Coastguard Worker     bool IsEmpty(const HashedKey<StoreKey>& key) const {
149*795d594fSAndroid Build Coastguard Worker       return key.IsEmpty();
150*795d594fSAndroid Build Coastguard Worker     }
151*795d594fSAndroid Build Coastguard Worker 
152*795d594fSAndroid Build Coastguard Worker     void MakeEmpty(HashedKey<StoreKey>& key) {
153*795d594fSAndroid Build Coastguard Worker       key.MakeEmpty();
154*795d594fSAndroid Build Coastguard Worker     }
155*795d594fSAndroid Build Coastguard Worker   };
156*795d594fSAndroid Build Coastguard Worker 
157*795d594fSAndroid Build Coastguard Worker   struct ShardHashFn {
158*795d594fSAndroid Build Coastguard Worker     template <typename T>
159*795d594fSAndroid Build Coastguard Worker     size_t operator()(const HashedKey<T>& key) const {
160*795d594fSAndroid Build Coastguard Worker       return key.Hash();
161*795d594fSAndroid Build Coastguard Worker     }
162*795d594fSAndroid Build Coastguard Worker   };
163*795d594fSAndroid Build Coastguard Worker 
164*795d594fSAndroid Build Coastguard Worker   struct ShardPred {
165*795d594fSAndroid Build Coastguard Worker     typename std::enable_if<!std::is_same<StoreKey, InKey>::value, bool>::type
166*795d594fSAndroid Build Coastguard Worker     operator()(const HashedKey<StoreKey>& lhs, const HashedKey<StoreKey>& rhs) const {
167*795d594fSAndroid Build Coastguard Worker       DCHECK(lhs.Key() != nullptr);
168*795d594fSAndroid Build Coastguard Worker       DCHECK(rhs.Key() != nullptr);
169*795d594fSAndroid Build Coastguard Worker       // Rehashing: stored keys are already deduplicated, so we can simply compare key pointers.
170*795d594fSAndroid Build Coastguard Worker       return lhs.Key() == rhs.Key();
171*795d594fSAndroid Build Coastguard Worker     }
172*795d594fSAndroid Build Coastguard Worker 
173*795d594fSAndroid Build Coastguard Worker     template <typename LeftT, typename RightT>
174*795d594fSAndroid Build Coastguard Worker     bool operator()(const HashedKey<LeftT>& lhs, const HashedKey<RightT>& rhs) const {
175*795d594fSAndroid Build Coastguard Worker       DCHECK(lhs.Key() != nullptr);
176*795d594fSAndroid Build Coastguard Worker       DCHECK(rhs.Key() != nullptr);
177*795d594fSAndroid Build Coastguard Worker       return lhs.Hash() == rhs.Hash() &&
178*795d594fSAndroid Build Coastguard Worker           lhs.Key()->size() == rhs.Key()->size() &&
179*795d594fSAndroid Build Coastguard Worker           std::equal(lhs.Key()->begin(), lhs.Key()->end(), rhs.Key()->begin());
180*795d594fSAndroid Build Coastguard Worker     }
181*795d594fSAndroid Build Coastguard Worker   };
182*795d594fSAndroid Build Coastguard Worker 
183*795d594fSAndroid Build Coastguard Worker   Alloc alloc_;
184*795d594fSAndroid Build Coastguard Worker   const std::string lock_name_;
185*795d594fSAndroid Build Coastguard Worker   Mutex lock_;
186*795d594fSAndroid Build Coastguard Worker   HashSet<HashedKey<StoreKey>, ShardEmptyFn, ShardHashFn, ShardPred> keys_ GUARDED_BY(lock_);
187*795d594fSAndroid Build Coastguard Worker };
188*795d594fSAndroid Build Coastguard Worker 
189*795d594fSAndroid Build Coastguard Worker template <typename InKey,
190*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
191*795d594fSAndroid Build Coastguard Worker           typename Alloc,
192*795d594fSAndroid Build Coastguard Worker           typename HashType,
193*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
194*795d594fSAndroid Build Coastguard Worker           HashType kShard>
195*795d594fSAndroid Build Coastguard Worker const StoreKey* DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Add(
196*795d594fSAndroid Build Coastguard Worker     Thread* self, const InKey& key) {
197*795d594fSAndroid Build Coastguard Worker   uint64_t hash_start;
198*795d594fSAndroid Build Coastguard Worker   if (kIsDebugBuild) {
199*795d594fSAndroid Build Coastguard Worker     hash_start = NanoTime();
200*795d594fSAndroid Build Coastguard Worker   }
201*795d594fSAndroid Build Coastguard Worker   HashType raw_hash = HashFunc()(key);
202*795d594fSAndroid Build Coastguard Worker   if (kIsDebugBuild) {
203*795d594fSAndroid Build Coastguard Worker     uint64_t hash_end = NanoTime();
204*795d594fSAndroid Build Coastguard Worker     hash_time_ += hash_end - hash_start;
205*795d594fSAndroid Build Coastguard Worker   }
206*795d594fSAndroid Build Coastguard Worker   HashType shard_hash = raw_hash / kShard;
207*795d594fSAndroid Build Coastguard Worker   HashType shard_bin = raw_hash % kShard;
208*795d594fSAndroid Build Coastguard Worker   return shards_[shard_bin]->Add(self, shard_hash, key);
209*795d594fSAndroid Build Coastguard Worker }
210*795d594fSAndroid Build Coastguard Worker 
211*795d594fSAndroid Build Coastguard Worker template <typename InKey,
212*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
213*795d594fSAndroid Build Coastguard Worker           typename Alloc,
214*795d594fSAndroid Build Coastguard Worker           typename HashType,
215*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
216*795d594fSAndroid Build Coastguard Worker           HashType kShard>
217*795d594fSAndroid Build Coastguard Worker DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DedupeSet(const char* set_name,
218*795d594fSAndroid Build Coastguard Worker                                                                          const Alloc& alloc)
219*795d594fSAndroid Build Coastguard Worker     : hash_time_(0) {
220*795d594fSAndroid Build Coastguard Worker   for (HashType i = 0; i < kShard; ++i) {
221*795d594fSAndroid Build Coastguard Worker     std::ostringstream oss;
222*795d594fSAndroid Build Coastguard Worker     oss << set_name << " lock " << i;
223*795d594fSAndroid Build Coastguard Worker     shards_[i].reset(new Shard(alloc, oss.str()));
224*795d594fSAndroid Build Coastguard Worker   }
225*795d594fSAndroid Build Coastguard Worker }
226*795d594fSAndroid Build Coastguard Worker 
227*795d594fSAndroid Build Coastguard Worker template <typename InKey,
228*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
229*795d594fSAndroid Build Coastguard Worker           typename Alloc,
230*795d594fSAndroid Build Coastguard Worker           typename HashType,
231*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
232*795d594fSAndroid Build Coastguard Worker           HashType kShard>
233*795d594fSAndroid Build Coastguard Worker DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::~DedupeSet() {
234*795d594fSAndroid Build Coastguard Worker   // Everything done by member destructors.
235*795d594fSAndroid Build Coastguard Worker }
236*795d594fSAndroid Build Coastguard Worker 
237*795d594fSAndroid Build Coastguard Worker template <typename InKey,
238*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
239*795d594fSAndroid Build Coastguard Worker           typename Alloc,
240*795d594fSAndroid Build Coastguard Worker           typename HashType,
241*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
242*795d594fSAndroid Build Coastguard Worker           HashType kShard>
243*795d594fSAndroid Build Coastguard Worker size_t DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Size(Thread* self) const {
244*795d594fSAndroid Build Coastguard Worker   size_t result = 0u;
245*795d594fSAndroid Build Coastguard Worker   for (const auto& shard : shards_) {
246*795d594fSAndroid Build Coastguard Worker     result += shard->Size(self);
247*795d594fSAndroid Build Coastguard Worker   }
248*795d594fSAndroid Build Coastguard Worker   return result;
249*795d594fSAndroid Build Coastguard Worker }
250*795d594fSAndroid Build Coastguard Worker 
251*795d594fSAndroid Build Coastguard Worker template <typename InKey,
252*795d594fSAndroid Build Coastguard Worker           typename StoreKey,
253*795d594fSAndroid Build Coastguard Worker           typename Alloc,
254*795d594fSAndroid Build Coastguard Worker           typename HashType,
255*795d594fSAndroid Build Coastguard Worker           typename HashFunc,
256*795d594fSAndroid Build Coastguard Worker           HashType kShard>
257*795d594fSAndroid Build Coastguard Worker std::string DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DumpStats(
258*795d594fSAndroid Build Coastguard Worker     Thread* self) const {
259*795d594fSAndroid Build Coastguard Worker   Stats stats;
260*795d594fSAndroid Build Coastguard Worker   for (HashType shard = 0; shard < kShard; ++shard) {
261*795d594fSAndroid Build Coastguard Worker     shards_[shard]->UpdateStats(self, &stats);
262*795d594fSAndroid Build Coastguard Worker   }
263*795d594fSAndroid Build Coastguard Worker   return android::base::StringPrintf("%zu collisions, %zu max hash collisions, "
264*795d594fSAndroid Build Coastguard Worker                                      "%zu/%zu probe distance, %" PRIu64 " ns hash time",
265*795d594fSAndroid Build Coastguard Worker                                      stats.collision_sum,
266*795d594fSAndroid Build Coastguard Worker                                      stats.collision_max,
267*795d594fSAndroid Build Coastguard Worker                                      stats.total_probe_distance,
268*795d594fSAndroid Build Coastguard Worker                                      stats.total_size,
269*795d594fSAndroid Build Coastguard Worker                                      hash_time_);
270*795d594fSAndroid Build Coastguard Worker }
271*795d594fSAndroid Build Coastguard Worker 
272*795d594fSAndroid Build Coastguard Worker 
273*795d594fSAndroid Build Coastguard Worker }  // namespace art
274*795d594fSAndroid Build Coastguard Worker 
275*795d594fSAndroid Build Coastguard Worker #endif  // ART_COMPILER_UTILS_DEDUPE_SET_INL_H_
276