Skip to content

Commit 2283da8

Browse files
authored
Added int2int/str2int memory-mappable hashmap based on 3rd pty Ankerl Hashmap (#222)
1 parent ba602a4 commit 2283da8

9 files changed

Lines changed: 2873 additions & 3 deletions

File tree

THIRD-PARTY-LICENSES.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,27 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
121121
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
122122
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
123123
SOFTWARE.
124+
125+
------
126+
127+
** ankerl; version 4.0.0 -- https://github.com/martinus/unordered_dense
128+
Copyright © 2022-2023 Martin Leitner-Ankerl. The code is licensed under the MIT
129+
License.
130+
131+
Permission is hereby granted, free of charge, to any person obtaining a copy
132+
of this software and associated documentation files (the "Software"), to deal
133+
in the Software without restriction, including without limitation the rights
134+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
135+
copies of the Software, and to permit persons to whom the Software is
136+
furnished to do so, subject to the following conditions:
137+
138+
The above copyright notice and this permission notice shall be included in all
139+
copies or substantial portions of the Software.
140+
141+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
142+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
143+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
144+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
145+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
146+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
147+
SOFTWARE.

pecos/core/base.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
c_int32,
2626
c_uint32,
2727
c_uint64,
28+
c_size_t,
2829
c_void_p,
2930
cast,
3031
)
@@ -531,6 +532,7 @@ def __init__(self, dirname, soname, forced_rebuild=False):
531532
self.link_clustering()
532533
self.link_tfidf_vectorizer()
533534
self.link_ann_hnsw_methods()
535+
self.link_mmap_hashmap_methods()
534536

535537
def link_xlinear_methods(self):
536538
"""
@@ -1699,5 +1701,84 @@ def ann_hnsw_init(self, data_type, metric_type):
16991701
)
17001702
return self.ann_hnsw_fn_dict[data_type, metric_type]
17011703

1704+
def link_mmap_hashmap_methods(self):
1705+
"""
1706+
Specify C-lib's Memory-mappable Hashmap methods arguments and return types.
1707+
"""
1708+
fn_prefix = "mmap_hashmap"
1709+
map_type_list = ["str2int", "int2int"]
1710+
key_args_dict = {
1711+
"str2int": [
1712+
c_char_p, # pointer of key string
1713+
c_uint32, # length of key string
1714+
],
1715+
"int2int": [
1716+
c_uint64, # key int64
1717+
],
1718+
}
1719+
self.mmap_map_fn_dict = {}
1720+
1721+
for map_type in map_type_list:
1722+
local_fn_dict = {}
1723+
1724+
fn_name = "new"
1725+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1726+
corelib.fillprototype(local_fn_dict[fn_name], c_void_p, None)
1727+
1728+
fn_name = "destruct"
1729+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1730+
corelib.fillprototype(local_fn_dict[fn_name], None, [c_void_p])
1731+
1732+
fn_name = "save"
1733+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1734+
corelib.fillprototype(local_fn_dict[fn_name], None, [c_void_p, c_char_p])
1735+
1736+
fn_name = "load"
1737+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1738+
corelib.fillprototype(local_fn_dict[fn_name], c_void_p, [c_char_p, c_bool])
1739+
1740+
fn_name = "size"
1741+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1742+
corelib.fillprototype(local_fn_dict[fn_name], c_size_t, [c_void_p])
1743+
1744+
# Fill insert & get
1745+
fn_name = "insert"
1746+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1747+
corelib.fillprototype(
1748+
local_fn_dict[fn_name], None, [c_void_p] + key_args_dict[map_type] + [c_uint64]
1749+
)
1750+
1751+
fn_name = "get"
1752+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1753+
corelib.fillprototype(
1754+
local_fn_dict[fn_name], c_uint64, [c_void_p] + key_args_dict[map_type]
1755+
)
1756+
1757+
fn_name = "get_w_default"
1758+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1759+
corelib.fillprototype(
1760+
local_fn_dict[fn_name], c_uint64, [c_void_p] + key_args_dict[map_type] + [c_uint64]
1761+
)
1762+
1763+
fn_name = "contains"
1764+
local_fn_dict[fn_name] = getattr(self.clib_float32, f"{fn_prefix}_{fn_name}_{map_type}")
1765+
corelib.fillprototype(
1766+
local_fn_dict[fn_name], c_bool, [c_void_p] + key_args_dict[map_type]
1767+
)
1768+
1769+
self.mmap_map_fn_dict[map_type] = local_fn_dict
1770+
1771+
def mmap_hashmap_init(self, map_type):
1772+
"""Python to C/C++ interface for Memory-mappable Hashmap initialization
1773+
Args:
1774+
map_type (string): Type of Hashmap.
1775+
Returns:
1776+
mmap_map_fn_dict (dict): a dictionary that holds clib's C/C++ functions for Python to call
1777+
"""
1778+
1779+
if map_type not in self.mmap_map_fn_dict:
1780+
raise NotImplementedError(f"map_type={map_type} is not implemented.")
1781+
return self.mmap_map_fn_dict[map_type]
1782+
17021783

17031784
clib = corelib(os.path.join(os.path.dirname(os.path.abspath(pecos.__file__)), "core"), "libpecos")

pecos/core/libpecos.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "utils/clustering.hpp"
1515
#include "utils/matrix.hpp"
16+
#include "utils/mmap_hashmap.hpp"
1617
#include "utils/tfidf.hpp"
1718
#include "utils/parallel.hpp"
1819
#include "xmc/inference.hpp"
@@ -474,4 +475,73 @@ extern "C" {
474475
C_ANN_HNSW_PREDICT(_csr_ip_f32, ScipyCsrF32, pecos::csr_t, hnsw_csr_ip_t)
475476
C_ANN_HNSW_PREDICT(_csr_l2_f32, ScipyCsrF32, pecos::csr_t, hnsw_csr_l2_t)
476477

478+
// ==== C Interface of Memory-mappable Hashmap ====
479+
480+
typedef pecos::mmap_hashmap::Str2IntMap mmap_hashmap_str2int;
481+
typedef pecos::mmap_hashmap::Int2IntMap mmap_hashmap_int2int;
482+
483+
// New
484+
#define MMAP_MAP_NEW(SUFFIX) \
485+
void* mmap_hashmap_new_ ## SUFFIX () { \
486+
return static_cast<void*>(new mmap_hashmap_ ## SUFFIX()); }
487+
MMAP_MAP_NEW(str2int)
488+
MMAP_MAP_NEW(int2int)
489+
490+
// Destruct
491+
#define MMAP_MAP_DESTRUCT(SUFFIX) \
492+
void mmap_hashmap_destruct_ ## SUFFIX (void* map_ptr) { \
493+
delete static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr); }
494+
MMAP_MAP_DESTRUCT(str2int)
495+
MMAP_MAP_DESTRUCT(int2int)
496+
497+
// Save
498+
#define MMAP_MAP_SAVE(SUFFIX) \
499+
void mmap_hashmap_save_ ## SUFFIX (void* map_ptr, const char* map_dir) { \
500+
static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr)->save(map_dir); }
501+
MMAP_MAP_SAVE(str2int)
502+
MMAP_MAP_SAVE(int2int)
503+
504+
// Load
505+
#define MMAP_MAP_LOAD(SUFFIX) \
506+
void* mmap_hashmap_load_ ## SUFFIX (const char* map_dir, const bool lazy_load) { \
507+
mmap_hashmap_ ## SUFFIX * map_ptr = new mmap_hashmap_ ## SUFFIX(); \
508+
map_ptr->load(map_dir, lazy_load); \
509+
return static_cast<void *>(map_ptr); }
510+
MMAP_MAP_LOAD(str2int)
511+
MMAP_MAP_LOAD(int2int)
512+
513+
// Size
514+
#define MMAP_MAP_SIZE(SUFFIX) \
515+
size_t mmap_hashmap_size_ ## SUFFIX (void* map_ptr) { \
516+
return static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr)->size(); }
517+
MMAP_MAP_SIZE(str2int)
518+
MMAP_MAP_SIZE(int2int)
519+
520+
// Insert
521+
#define KEY_SINGLE_ARG(A,B) A,B
522+
#define MMAP_MAP_INSERT(SUFFIX, KEY, FUNC_CALL_KEY) \
523+
void mmap_hashmap_insert_ ## SUFFIX (void* map_ptr, KEY, uint64_t val) { \
524+
static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr)->insert(FUNC_CALL_KEY, val); }
525+
MMAP_MAP_INSERT(str2int, KEY_SINGLE_ARG(const char* key, uint32_t key_len), KEY_SINGLE_ARG(key, key_len))
526+
MMAP_MAP_INSERT(int2int, uint64_t key, key)
527+
528+
// Get
529+
#define MMAP_MAP_GET(SUFFIX, KEY, FUNC_CALL_KEY) \
530+
uint64_t mmap_hashmap_get_ ## SUFFIX (void* map_ptr, KEY) { \
531+
return static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr)->get(FUNC_CALL_KEY); }
532+
MMAP_MAP_GET(str2int, KEY_SINGLE_ARG(const char* key, uint32_t key_len), KEY_SINGLE_ARG(key, key_len))
533+
MMAP_MAP_GET(int2int, uint64_t key, key)
534+
535+
#define MMAP_MAP_GET_W_DEFAULT(SUFFIX, KEY, FUNC_CALL_KEY) \
536+
uint64_t mmap_hashmap_get_w_default_ ## SUFFIX (void* map_ptr, KEY, uint64_t def_val) { \
537+
return static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr)->get_w_default(FUNC_CALL_KEY, def_val); }
538+
MMAP_MAP_GET_W_DEFAULT(str2int, KEY_SINGLE_ARG(const char* key, uint32_t key_len), KEY_SINGLE_ARG(key, key_len))
539+
MMAP_MAP_GET_W_DEFAULT(int2int, uint64_t key, key)
540+
541+
// Contains
542+
#define MMAP_MAP_CONTAINS(SUFFIX, KEY, FUNC_CALL_KEY) \
543+
bool mmap_hashmap_contains_ ## SUFFIX (void* map_ptr, KEY) { \
544+
return static_cast<mmap_hashmap_ ## SUFFIX *>(map_ptr)->contains(FUNC_CALL_KEY); }
545+
MMAP_MAP_CONTAINS(str2int, KEY_SINGLE_ARG(const char* key, uint32_t key_len), KEY_SINGLE_ARG(key, key_len))
546+
MMAP_MAP_CONTAINS(int2int, uint64_t key, key)
477547
}

0 commit comments

Comments
 (0)