/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_COMPILER_UTILS_DEDUPE_SET_INL_H_ #define ART_COMPILER_UTILS_DEDUPE_SET_INL_H_ #include "dedupe_set.h" #include #include #include #include "android-base/stringprintf.h" #include "base/hash_set.h" #include "base/mutex.h" #include "base/stl_util.h" #include "base/time_utils.h" namespace art { template struct DedupeSet::Stats { size_t collision_sum = 0u; size_t collision_max = 0u; size_t total_probe_distance = 0u; size_t total_size = 0u; }; template class DedupeSet::Shard { public: Shard(const Alloc& alloc, const std::string& lock_name) : alloc_(alloc), lock_name_(lock_name), lock_(lock_name_.c_str()), keys_() { } ~Shard() { for (const HashedKey& key : keys_) { DCHECK(key.Key() != nullptr); alloc_.Destroy(key.Key()); } } const StoreKey* Add(Thread* self, size_t hash, const InKey& in_key) REQUIRES(!lock_) { MutexLock lock(self, lock_); HashedKey hashed_in_key(hash, &in_key); auto it = keys_.find(hashed_in_key); if (it != keys_.end()) { DCHECK(it->Key() != nullptr); return it->Key(); } const StoreKey* store_key = alloc_.Copy(in_key); keys_.insert(HashedKey { hash, store_key }); return store_key; } void UpdateStats(Thread* self, Stats* global_stats) REQUIRES(!lock_) { // HashSet<> doesn't keep entries ordered by hash, so we actually allocate memory // for bookkeeping while collecting the stats. std::unordered_map stats; { MutexLock lock(self, lock_); // Note: The total_probe_distance will be updated with the current state. // It may have been higher before a re-hash. global_stats->total_probe_distance += keys_.TotalProbeDistance(); global_stats->total_size += keys_.size(); for (const HashedKey& key : keys_) { auto it = stats.find(key.Hash()); if (it == stats.end()) { stats.insert({key.Hash(), 1u}); } else { ++it->second; } } } for (const auto& entry : stats) { size_t number_of_entries = entry.second; if (number_of_entries > 1u) { global_stats->collision_sum += number_of_entries - 1u; global_stats->collision_max = std::max(global_stats->collision_max, number_of_entries); } } } private: template class HashedKey { public: HashedKey() : hash_(0u), key_(nullptr) { } HashedKey(size_t hash, const T* key) : hash_(hash), key_(key) { } size_t Hash() const { return hash_; } const T* Key() const { return key_; } bool IsEmpty() const { return Key() == nullptr; } void MakeEmpty() { key_ = nullptr; } private: size_t hash_; const T* key_; }; class ShardEmptyFn { public: bool IsEmpty(const HashedKey& key) const { return key.IsEmpty(); } void MakeEmpty(HashedKey& key) { key.MakeEmpty(); } }; struct ShardHashFn { template size_t operator()(const HashedKey& key) const { return key.Hash(); } }; struct ShardPred { typename std::enable_if::value, bool>::type operator()(const HashedKey& lhs, const HashedKey& rhs) const { DCHECK(lhs.Key() != nullptr); DCHECK(rhs.Key() != nullptr); // Rehashing: stored keys are already deduplicated, so we can simply compare key pointers. return lhs.Key() == rhs.Key(); } template bool operator()(const HashedKey& lhs, const HashedKey& rhs) const { DCHECK(lhs.Key() != nullptr); DCHECK(rhs.Key() != nullptr); return lhs.Hash() == rhs.Hash() && lhs.Key()->size() == rhs.Key()->size() && std::equal(lhs.Key()->begin(), lhs.Key()->end(), rhs.Key()->begin()); } }; Alloc alloc_; const std::string lock_name_; Mutex lock_; HashSet, ShardEmptyFn, ShardHashFn, ShardPred> keys_ GUARDED_BY(lock_); }; template const StoreKey* DedupeSet::Add( Thread* self, const InKey& key) { uint64_t hash_start; if (kIsDebugBuild) { hash_start = NanoTime(); } HashType raw_hash = HashFunc()(key); if (kIsDebugBuild) { uint64_t hash_end = NanoTime(); hash_time_ += hash_end - hash_start; } HashType shard_hash = raw_hash / kShard; HashType shard_bin = raw_hash % kShard; return shards_[shard_bin]->Add(self, shard_hash, key); } template DedupeSet::DedupeSet(const char* set_name, const Alloc& alloc) : hash_time_(0) { for (HashType i = 0; i < kShard; ++i) { std::ostringstream oss; oss << set_name << " lock " << i; shards_[i].reset(new Shard(alloc, oss.str())); } } template DedupeSet::~DedupeSet() { // Everything done by member destructors. } template std::string DedupeSet::DumpStats( Thread* self) const { Stats stats; for (HashType shard = 0; shard < kShard; ++shard) { shards_[shard]->UpdateStats(self, &stats); } return android::base::StringPrintf("%zu collisions, %zu max hash collisions, " "%zu/%zu probe distance, %" PRIu64 " ns hash time", stats.collision_sum, stats.collision_max, stats.total_probe_distance, stats.total_size, hash_time_); } } // namespace art #endif // ART_COMPILER_UTILS_DEDUPE_SET_INL_H_