172 lines
5.7 KiB
C++
172 lines
5.7 KiB
C++
/*
|
|
* Copyright (C) 2013 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_H_
|
|
#define ART_COMPILER_UTILS_DEDUPE_SET_H_
|
|
|
|
#include <algorithm>
|
|
#include <inttypes.h>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#include "base/mutex.h"
|
|
#include "base/stl_util.h"
|
|
#include "base/stringprintf.h"
|
|
#include "utils/swap_space.h"
|
|
|
|
namespace art {
|
|
|
|
// A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
|
|
// Add method. The data-structure is thread-safe through the use of internal locks, it also
|
|
// supports the lock being sharded.
|
|
template <typename InKey, typename StoreKey, typename HashType, typename HashFunc,
|
|
HashType kShard = 1>
|
|
class DedupeSet {
|
|
typedef std::pair<HashType, const InKey*> HashedInKey;
|
|
struct HashedKey {
|
|
StoreKey* store_ptr;
|
|
union {
|
|
HashType store_hash; // Valid if store_ptr != nullptr.
|
|
const HashedInKey* in_key; // Valid if store_ptr == nullptr.
|
|
};
|
|
};
|
|
|
|
class Comparator {
|
|
public:
|
|
bool operator()(const HashedKey& a, const HashedKey& b) const {
|
|
HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first;
|
|
HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first;
|
|
if (a_hash != b_hash) {
|
|
return a_hash < b_hash;
|
|
}
|
|
if (a.store_ptr != nullptr && b.store_ptr != nullptr) {
|
|
return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
|
|
b.store_ptr->begin(), b.store_ptr->end());
|
|
} else if (a.store_ptr != nullptr && b.store_ptr == nullptr) {
|
|
return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
|
|
b.in_key->second->begin(), b.in_key->second->end());
|
|
} else if (a.store_ptr == nullptr && b.store_ptr != nullptr) {
|
|
return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
|
|
b.store_ptr->begin(), b.store_ptr->end());
|
|
} else {
|
|
return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
|
|
b.in_key->second->begin(), b.in_key->second->end());
|
|
}
|
|
}
|
|
};
|
|
|
|
public:
|
|
StoreKey* 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;
|
|
HashedInKey hashed_in_key(shard_hash, &key);
|
|
HashedKey hashed_key;
|
|
hashed_key.store_ptr = nullptr;
|
|
hashed_key.in_key = &hashed_in_key;
|
|
MutexLock lock(self, *lock_[shard_bin]);
|
|
auto it = keys_[shard_bin].find(hashed_key);
|
|
if (it != keys_[shard_bin].end()) {
|
|
DCHECK(it->store_ptr != nullptr);
|
|
return it->store_ptr;
|
|
}
|
|
hashed_key.store_ptr = CreateStoreKey(key);
|
|
hashed_key.store_hash = shard_hash;
|
|
keys_[shard_bin].insert(hashed_key);
|
|
return hashed_key.store_ptr;
|
|
}
|
|
|
|
explicit DedupeSet(const char* set_name, SwapAllocator<void>& alloc)
|
|
: allocator_(alloc), hash_time_(0) {
|
|
for (HashType i = 0; i < kShard; ++i) {
|
|
std::ostringstream oss;
|
|
oss << set_name << " lock " << i;
|
|
lock_name_[i] = oss.str();
|
|
lock_[i].reset(new Mutex(lock_name_[i].c_str()));
|
|
}
|
|
}
|
|
|
|
~DedupeSet() {
|
|
// Have to manually free all pointers.
|
|
for (auto& shard : keys_) {
|
|
for (const auto& hashed_key : shard) {
|
|
DCHECK(hashed_key.store_ptr != nullptr);
|
|
DeleteStoreKey(hashed_key.store_ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string DumpStats() const {
|
|
size_t collision_sum = 0;
|
|
size_t collision_max = 0;
|
|
for (HashType shard = 0; shard < kShard; ++shard) {
|
|
HashType last_hash = 0;
|
|
size_t collision_cur_max = 0;
|
|
for (const HashedKey& key : keys_[shard]) {
|
|
DCHECK(key.store_ptr != nullptr);
|
|
if (key.store_hash == last_hash) {
|
|
collision_cur_max++;
|
|
if (collision_cur_max > 1) {
|
|
collision_sum++;
|
|
if (collision_cur_max > collision_max) {
|
|
collision_max = collision_cur_max;
|
|
}
|
|
}
|
|
} else {
|
|
collision_cur_max = 1;
|
|
last_hash = key.store_hash;
|
|
}
|
|
}
|
|
}
|
|
return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time",
|
|
collision_sum, collision_max, hash_time_);
|
|
}
|
|
|
|
private:
|
|
StoreKey* CreateStoreKey(const InKey& key) {
|
|
StoreKey* ret = allocator_.allocate(1);
|
|
allocator_.construct(ret, key.begin(), key.end(), allocator_);
|
|
return ret;
|
|
}
|
|
|
|
void DeleteStoreKey(StoreKey* key) {
|
|
SwapAllocator<StoreKey> alloc(allocator_);
|
|
alloc.destroy(key);
|
|
alloc.deallocate(key, 1);
|
|
}
|
|
|
|
std::string lock_name_[kShard];
|
|
std::unique_ptr<Mutex> lock_[kShard];
|
|
std::set<HashedKey, Comparator> keys_[kShard];
|
|
SwapAllocator<StoreKey> allocator_;
|
|
uint64_t hash_time_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DedupeSet);
|
|
};
|
|
|
|
} // namespace art
|
|
|
|
#endif // ART_COMPILER_UTILS_DEDUPE_SET_H_
|