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