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