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