mirror of
https://github.com/greg7mdp/parallel-hashmap.git
synced 2026-08-30 00:50:38 +08:00
added load&dump interface
This commit is contained in:
Vendored
+1
@@ -5,4 +5,5 @@ benchmark/charts.html
|
||||
build
|
||||
build_linux
|
||||
.vagrant
|
||||
**/.vscode
|
||||
TAGS
|
||||
|
||||
Vendored
+3
@@ -17,6 +17,7 @@ set(CMAKE_SUPPRESS_REGENERATION true) ## suppress ZERO_CHECK project
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(helpers)
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
|
||||
@@ -130,6 +131,8 @@ if (PHMAP_BUILD_EXAMPLES)
|
||||
add_executable(ex_two_files examples/f1.cc examples/f2.cc phmap.natvis)
|
||||
add_executable(ex_insert_bench examples/insert_bench.cc phmap.natvis)
|
||||
add_executable(ex_knucleotide examples/knucleotide.cc phmap.natvis)
|
||||
add_executable(ex_dump_load examples/dump_load.cc phmap.natvis)
|
||||
|
||||
target_link_libraries(ex_knucleotide Threads::Threads)
|
||||
target_link_libraries(ex_bench Threads::Threads)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <parallel_hashmap/phmap.h>
|
||||
|
||||
using phmap::flat_hash_map;
|
||||
using phmap::flat_hash_set;
|
||||
|
||||
void load_dump_string_string() {
|
||||
flat_hash_map<std::string, std::string> mp1;
|
||||
|
||||
// Add a new entry
|
||||
mp1["key-1"] = "value-1";
|
||||
mp1["key-2"] = "value-2";
|
||||
|
||||
// Iterate and print keys and values
|
||||
for (const auto& n : mp1)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
|
||||
mp1.dump("./dump.data");
|
||||
flat_hash_map<std::string, std::string> mp2;
|
||||
|
||||
mp2.load("./dump.data");
|
||||
// Iterate and print keys and values g|++
|
||||
for (const auto& n : mp2)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
}
|
||||
|
||||
void load_dump_uint64_uint32() {
|
||||
flat_hash_map<uint64_t, uint32_t> mp1;
|
||||
|
||||
// Add a new entry
|
||||
mp1[100] = 99;
|
||||
mp1[300] = 299;
|
||||
|
||||
// Iterate and print keys and values
|
||||
for (const auto& n : mp1)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
|
||||
mp1.dump("./dump.data");
|
||||
flat_hash_map<uint64_t, uint32_t> mp2;
|
||||
|
||||
mp2.load("./dump.data");
|
||||
// Iterate and print keys and values g|++
|
||||
for (const auto& n : mp2)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
}
|
||||
|
||||
void load_dump_string_uint32() {
|
||||
flat_hash_map<std::string, uint32_t> mp1;
|
||||
|
||||
// Add a new entry
|
||||
mp1["key-1"] = 99;
|
||||
mp1["key-2"] = 299;
|
||||
|
||||
// Iterate and print keys and values
|
||||
for (const auto& n : mp1)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
|
||||
mp1.dump("./dump.data");
|
||||
flat_hash_map<std::string, uint32_t> mp2;
|
||||
|
||||
mp2.load("./dump.data");
|
||||
// Iterate and print keys and values g|++
|
||||
for (const auto& n : mp2)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
}
|
||||
|
||||
void load_dump_uint32_string() {
|
||||
flat_hash_map<uint32_t, std::string> mp1;
|
||||
|
||||
// Add a new entry
|
||||
mp1[100] = "hello";
|
||||
mp1[299] = "world";
|
||||
|
||||
// Iterate and print keys and values
|
||||
for (const auto& n : mp1)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
|
||||
mp1.dump("./dump.data");
|
||||
flat_hash_map<uint32_t, std::string> mp2;
|
||||
|
||||
mp2.load("./dump.data");
|
||||
// Iterate and print keys and values g|++
|
||||
for (const auto& n : mp2)
|
||||
std::cout << n.first << "'s value is: " << n.second << "\n";
|
||||
}
|
||||
|
||||
void load_dump_string() {
|
||||
flat_hash_set<std::string> st1;
|
||||
|
||||
// Add a new entry
|
||||
st1.insert("hello");
|
||||
st1.insert("world");
|
||||
|
||||
// Iterate and print
|
||||
for (const auto& n : st1)
|
||||
std::cout << "value: " << n << "\n";
|
||||
|
||||
st1.dump("./dump.data");
|
||||
flat_hash_set<std::string> st2;
|
||||
|
||||
st2.load("./dump.data");
|
||||
// Iterate and print keys and values g|++
|
||||
for (const auto& n : st2)
|
||||
std::cout << "value: " << n << "\n";
|
||||
}
|
||||
|
||||
void load_dump_uint64() {
|
||||
flat_hash_set<uint64_t> st1;
|
||||
|
||||
// Add a new entry
|
||||
st1.insert(878);
|
||||
st1.insert(1424);
|
||||
|
||||
// Iterate and print
|
||||
for (const auto& n : st1)
|
||||
std::cout << "value: " << n << "\n";
|
||||
|
||||
st1.dump("./dump.data");
|
||||
flat_hash_set<uint64_t> st2;
|
||||
|
||||
st2.load("./dump.data");
|
||||
// Iterate and print keys and values g|++
|
||||
for (const auto& n : st2)
|
||||
std::cout << "value: " << n << "\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
load_dump_string_string();
|
||||
load_dump_uint64_uint32();
|
||||
load_dump_string_uint32();
|
||||
load_dump_uint32_string();
|
||||
load_dump_string();
|
||||
load_dump_uint64();
|
||||
std::remove("./dump.data");
|
||||
return 0;
|
||||
}
|
||||
Vendored
+148
@@ -45,6 +45,9 @@
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "phmap_utils.h"
|
||||
#include "phmap_base.h"
|
||||
@@ -1534,6 +1537,151 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template<typename V = value_type>
|
||||
typename std::enable_if<type_traits_internal::IsArithmeticType<V>::value, bool>::type
|
||||
dump(const std::string& dump_file) noexcept(
|
||||
IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
|
||||
(!AllocTraits::propagate_on_container_swap::value ||
|
||||
IsNoThrowSwappable<allocator_type>())) {
|
||||
if (size_ == 0) {
|
||||
std::cout << "Empty set, nothing to dump" << std::endl;
|
||||
return true;
|
||||
}
|
||||
assert(slots_ != nullptr);
|
||||
std::ofstream ofs(dump_file);
|
||||
if (!ofs.is_open()) {
|
||||
std::cout << "Failed to open dump file " << dump_file << std::endl;
|
||||
return false;
|
||||
}
|
||||
ofs.write(reinterpret_cast<char*>(&size_), sizeof(size_));
|
||||
ofs.write(reinterpret_cast<char*>(&capacity_), sizeof(capacity_));
|
||||
ofs.write(reinterpret_cast<char*>(ctrl_), capacity_ * sizeof(ctrl_t));
|
||||
ofs.write(reinterpret_cast<char*>(slots_), capacity_ * sizeof(slot_type));
|
||||
ofs.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V = value_type>
|
||||
typename std::enable_if<type_traits_internal::IsArithmeticType<V>::value, bool>::type
|
||||
load(const std::string& load_file) noexcept(
|
||||
IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
|
||||
(!AllocTraits::propagate_on_container_swap::value ||
|
||||
IsNoThrowSwappable<allocator_type>())) {
|
||||
std::ifstream ifs(load_file);
|
||||
if (!ifs.is_open()) {
|
||||
std::cerr << "Failed to open load file " << load_file << std::endl;
|
||||
return false;
|
||||
}
|
||||
// get file size
|
||||
ifs.seekg(0, std::ios::end);
|
||||
size_t file_size = ifs.tellg();
|
||||
ifs.seekg(0, std::ios::beg);
|
||||
if (file_size <= sizeof(size_) + sizeof(capacity_)) {
|
||||
std::cerr << "Invalid file format. file size: " << file_size << ", size_: "
|
||||
<< size_ << ", capacity_: " << capacity_ << ", slot type size: "
|
||||
<< sizeof(slot_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
ifs.read(reinterpret_cast<char*>(&size_), sizeof(size_));
|
||||
ifs.read(reinterpret_cast<char*>(&capacity_), sizeof(capacity_));
|
||||
if (file_size != sizeof(size_) + sizeof(capacity_) + capacity_ * sizeof(ctrl_t)
|
||||
+ capacity_ * sizeof(slot_type)) {
|
||||
std::cerr << "Invalid file format. file size: " << file_size << ", size_: "
|
||||
<< size_ << ", capacity_: " << capacity_ << ", slot type size: "
|
||||
<< sizeof(slot_type);
|
||||
return false;
|
||||
}
|
||||
// allocate memory for ctrl_ and slots_
|
||||
initialize_slots();
|
||||
|
||||
ifs.read(reinterpret_cast<char*>(ctrl_), capacity_ * sizeof(ctrl_t));
|
||||
ifs.read(reinterpret_cast<char*>(slots_), capacity_ * sizeof(slot_type));
|
||||
ifs.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
// V will be V for hash_set and std::pair<const K, V> for hash_map
|
||||
template<typename V = value_type>
|
||||
typename std::enable_if<! type_traits_internal::IsArithmeticType<V>::value
|
||||
&& type_traits_internal::IsStringOrArithmeticType<V>::value, bool>::type
|
||||
dump(const std::string& dump_file) noexcept(
|
||||
IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
|
||||
(!AllocTraits::propagate_on_container_swap::value ||
|
||||
IsNoThrowSwappable<allocator_type>())) {
|
||||
if (size_ == 0) {
|
||||
std::cout << "Empty set, nothing to dump" << std::endl;
|
||||
return true;
|
||||
}
|
||||
assert(slots_ != nullptr);
|
||||
std::ofstream ofs(dump_file);
|
||||
if (!ofs.is_open()) {
|
||||
std::cout << "Failed to open dump file " << dump_file << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ofs.write(reinterpret_cast<char*>(&size_), sizeof(size_));
|
||||
|
||||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
type_traits_internal::Archive<V>::dump(*it, &ofs);
|
||||
}
|
||||
ofs.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V = value_type>
|
||||
typename std::enable_if<! type_traits_internal::IsArithmeticType<V>::value
|
||||
&& type_traits_internal::IsStringOrArithmeticType<V>::value, bool>::type
|
||||
load(const std::string& load_file = "") noexcept(
|
||||
IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
|
||||
(!AllocTraits::propagate_on_container_swap::value ||
|
||||
IsNoThrowSwappable<allocator_type>())) {
|
||||
|
||||
std::ifstream ifs(load_file);
|
||||
if (!ifs.is_open()) {
|
||||
std::cerr << "Failed to open load file " << load_file << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t total_count = 0;
|
||||
ifs.read((char*)&total_count, sizeof(total_count));
|
||||
|
||||
for (size_t i = 0; i < total_count; i ++) {
|
||||
if (ifs.eof()) {
|
||||
std::cerr << "Data is not enough, total_count: " << total_count
|
||||
<< ", meet eof at index: " << i << std::endl;
|
||||
return false;
|
||||
}
|
||||
V v;
|
||||
type_traits_internal::Archive<V>::load(ifs, &v);
|
||||
this->insert(v);
|
||||
}
|
||||
ifs.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V = value_type>
|
||||
typename std::enable_if<!type_traits_internal::IsStringOrArithmeticType<V>::value, bool>::type
|
||||
dump(const std::string&) noexcept(
|
||||
IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
|
||||
(!AllocTraits::propagate_on_container_swap::value ||
|
||||
IsNoThrowSwappable<allocator_type>())) {
|
||||
std::cerr << "Does not support this type now!" << std::endl;
|
||||
std::abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename V = value_type>
|
||||
typename std::enable_if<!type_traits_internal::IsStringOrArithmeticType<V>::value, bool>::type
|
||||
load(const std::string&) noexcept(
|
||||
IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
|
||||
(!AllocTraits::propagate_on_container_swap::value ||
|
||||
IsNoThrowSwappable<allocator_type>())) {
|
||||
std::cerr << "Does not support this type now!" << std::endl;
|
||||
std::abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
void rehash(size_t n) {
|
||||
if (n == 0 && capacity_ == 0) return;
|
||||
if (n == 0 && size_ == 0) {
|
||||
|
||||
Vendored
+81
-1
@@ -33,7 +33,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
@@ -71,6 +70,87 @@ struct EqualTo
|
||||
|
||||
namespace type_traits_internal {
|
||||
|
||||
template<typename T>
|
||||
struct PairTrait : public std::false_type {
|
||||
using first_type = typename std::remove_cv<T>::type;
|
||||
using second_type = typename std::remove_cv<T>::type;
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct PairTrait<std::pair<T1, T2>>: public std::true_type {
|
||||
using first_type = typename std::remove_cv<T1>::type;
|
||||
using second_type = typename std::remove_cv<T2>::type;
|
||||
};
|
||||
|
||||
template<typename V>
|
||||
struct IsArithmeticType {
|
||||
static constexpr bool value = std::is_arithmetic<V>::value
|
||||
|| (PairTrait<V>::value &&
|
||||
std::is_arithmetic<typename PairTrait<V>::first_type>::value
|
||||
&& std::is_arithmetic<typename PairTrait<V>::second_type>::value);
|
||||
};
|
||||
|
||||
template<typename V>
|
||||
struct IsStringOrArithmeticType {
|
||||
static constexpr bool value = IsArithmeticType<V>::value
|
||||
|| std::is_same<V, std::string>::value
|
||||
|| (PairTrait<V>::value
|
||||
&& (std::is_arithmetic<typename PairTrait<V>::first_type>::value
|
||||
|| std::is_same<typename PairTrait<V>::first_type, std::string>::value)
|
||||
&& (std::is_arithmetic<typename PairTrait<V>::second_type>::value
|
||||
|| std::is_same<typename PairTrait<V>::second_type, std::string>::value));
|
||||
};
|
||||
|
||||
// only support std::is_arithmetic or std::string types
|
||||
template<typename T>
|
||||
struct Archive {
|
||||
template<typename V = T>
|
||||
static typename std::enable_if<std::is_arithmetic<V>::value, void>::type
|
||||
dump(const V& v, std::ofstream* ofs) {
|
||||
ofs->write(reinterpret_cast<char*>(const_cast<V*>(&v)), sizeof(V));
|
||||
}
|
||||
|
||||
template<typename V = T>
|
||||
static typename std::enable_if<std::is_arithmetic<V>::value, void>::type
|
||||
load(std::ifstream& ifs, V* v) {
|
||||
ifs.read(reinterpret_cast<char*>(v), sizeof(V));
|
||||
}
|
||||
|
||||
template<typename V = T>
|
||||
static typename std::enable_if<std::is_same<std::string, typename std::remove_cv<V>::type>::value, void>::type
|
||||
dump(const V& v, std::ofstream* ofs) {
|
||||
uint32_t sz = v.length();
|
||||
ofs->write(reinterpret_cast<char*>(&sz), sizeof(sz));
|
||||
ofs->write(const_cast<char*>(v.data()), sz);
|
||||
}
|
||||
|
||||
template<typename V = T>
|
||||
static typename std::enable_if<std::is_same<std::string, typename std::remove_cv<V>::type>::value, void>::type
|
||||
load(std::ifstream& ifs, V* v) {
|
||||
uint32_t sz = 0;
|
||||
ifs.read(reinterpret_cast<char*>(&sz), sizeof(sz));
|
||||
const_cast<std::string*>(v)->resize(sz);
|
||||
ifs.read(const_cast<char*>(v->data()), sz);
|
||||
}
|
||||
|
||||
template<typename V = T>
|
||||
static typename std::enable_if<PairTrait<V>::value && IsStringOrArithmeticType<V>::value, void>::type
|
||||
dump(const V& v, std::ofstream* ofs) {
|
||||
dump<typename PairTrait<V>::first_type>(v.first, ofs);
|
||||
dump<typename PairTrait<V>::second_type>(v.second, ofs);
|
||||
}
|
||||
|
||||
template<typename V = T>
|
||||
static typename std::enable_if<PairTrait<V>::value && IsStringOrArithmeticType<V>::value, void>::type
|
||||
load(std::ifstream& ifs, V* v) {
|
||||
using first_type = typename PairTrait<V>::first_type;
|
||||
using second_type = typename PairTrait<V>::second_type;
|
||||
load<first_type>(ifs, const_cast<first_type*>(&v->first));
|
||||
load<second_type>(ifs, const_cast<second_type*>(&v->second));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename... Ts>
|
||||
struct VoidTImpl {
|
||||
using type = void;
|
||||
|
||||
Reference in New Issue
Block a user