plugin-system
NVE 插件系统
一、总体架构
NVE 的插件系统是一套 C 符号约定 + 抽象工厂 + 动态加载 的架构,用于提供多种宿主表(HostTable)存储后端。整体分三层:
┌──────────────────────────────────┐
│ build_host_database() │ ← 顶层编排函数
└────┬─────────────┬───────────────┘
│ │
┌────────────▼──┐ ┌──────▼──────────────┐
│ load_plugin()│ │ resolve_factory() │
│ dlopen .so │ │ htf_impls[] map │
└────┬──────────┘ └──────┬──────────────┘
│ │
┌────▼──────────────────────▼──────────────────┐
│ 静态注册的内置实现 │
│ "stl_map" / "map" / "umap" / "unordered_map"│
│ → STLMapTableFactory │
└───────────────────────────────────────────────┘
二、插件接口定义
插件 API 是一组 C 链接函数指针类型定义,而非 C++ 虚基类:
namespace nve {
using plugin_info_t = const char* (*)(); // 插件元信息
using enum_implementations_t = void (*)(void*, void (*)(void*, const char*)); // 枚举实现
using create_host_table_factory_t = host_table_factory_ptr_t (*)(const nlohmann::json&); // 工厂创建
} // namespace nve
每个插件 .so 必须导出以下 3 个 C 链接函数:
| 导出的 C 符号 | 签名 | 作用 |
|---|---|---|
plugin_ident() |
extern "C" const char*() |
返回插件人类可读名称 |
plugin_developer() |
extern "C" const char*() |
返回开发者名称 |
enum_host_table_implementations() |
extern "C" void(void*, void(*)(void*,const char*)) |
遍历注册插件内所有实现 |
create_{name}_table_factory() |
extern "C" host_table_factory_ptr_t(const nlohmann::json&) |
每个实现一个,创建工厂对象 |
这不是 C++ 虚基类,而是纯 C 符号约定——任何遵守这个符号约定的
.so都可以被dlopen+dlsym动态加载。
三、运行时加载机制
加载流程在 src/host_table.cpp 中实现,共 4 步:
Step 1 — dlopen 加载 .so
void load_host_table_plugin(const std::string_view& plugin_name) {
void* dll = dlopen(plugin_path.c_str(), RTLD_NOW | RTLD_GLOBAL);
// 如果给定路径失败,回退到相对于当前 .so 的路径
}
Step 2 — dlsym 解析 3 个必需符号
plugin_info_t plugin_ident = dlsym(dll, "plugin_ident");
plugin_info_t plugin_dev = dlsym(dll, "plugin_developer");
enum_implementations_t enum_ht = dlsym(dll, "enum_host_table_implementations");
Step 3 — 枚举注册实现
enum_ht(dll, register_implementation);
register_implementation 回调函数构造 "create_{name}_table_factory" 字符串,再次 dlsym 解析工厂构造函数,存入全局 std::unordered_map<string, create_host_table_factory_t>:
static std::unordered_map<std::string, create_host_table_factory_t> htf_impls;
Step 4 — 按 JSON 配置生产表
host_table_factory_ptr_t create_host_table_factory(const nlohmann::json& json);
host_table_ptr_t factory->produce(table_id_t id, const nlohmann::json& config);
四、类继承体系
┌──────────────────────────────────────────────────────────────┐
│ nve::Table (abstract) │
│ virtual clear() / erase() / find() / insert() / update() │
└──────────────────────────────────────────────────────────────┘
▲
┌──────────────────┴──────────────────┐
│ │
┌───────┴─────────────────────────┐ ┌────────┴────────────────┐
│ nve::HostTableLike │ │ GPU 端表 (抽象) │
│ + size() / config() 等 │ │ (gpu_table.hpp) │
└──────────────────────────────────┘ └─────────────────────────┘
▲
┌───────┴─────────────────────────┐
│ nve::HostTable<ConfigType> │ ← CRTP 基类,存储 config
└──────────────────────────────────┘
▲
├───────────────────────────────────┬───────────────────┐
│ │ │
┌───────┴──────────┐ ┌─────────────────────┴──┐ ┌─────────────┴──────────┐
│ STLContainerTable│ │ NvhmMapTable │ │ RedisClusterTable │
│ (umap / map) │ │ (GPU nvhashmap 后端) │ │ (远程 Redis 集群) │
└──────────────────┘ └────────────────────────┘ └────────────────────────┘
▲ ▲
AbseilFlatMapTable │
PHMapFlatMapTable │
(继承 STLContainerTable) │
│
┌────────┴──────────┐
│ RocksDBTable │
│ (磁盘 LSM-Tree) │
└───────────────────┘
关键区别:NVHM 插件直接继承
HostTable<NvhmMapTableConfig>,不走 STL 层。Abseil 和 PHMAP 继承STLContainerTable,复用其分片、超额淘汰、内存管理等基础设施。Redis 和 RocksDB 各自独立继承HostTable,因为它们操作的是远程网络连接或本地磁盘。
工厂类层次
HostTableLikeFactory (抽象工厂基类)
└── HostTableFactory<FactoryConfig, TableConfig> (模板工厂)
├── STLMapTableFactory → 内置(非插件)
├── NvhmMapTableFactory → NVHM 插件
├── AbseilFlatMapTableFactory → Abseil 插件
├── PHMapFlatMapTableFactory → PHMAP 插件
├── RedisClusterTableFactory → Redis 插件
└── RocksDBTableFactory → RocksDB 插件
五、5 个插件详解
5.1 NVHM 插件(libnve-plugin-nvhm.so)
| 属性 | 内容 |
|---|---|
| 注册名 | nvhm_map |
| 底层库 | NVIDIA nvhashmap — GPU 端开放寻址哈希表 |
| 存储位置 | GPU device memory |
| 数据持久化 | ❌ 不持久 |
核心配置:
| 字段 | 默认值 | 说明 |
|---|---|---|
num_partitions |
1 | 独立分片数(必须为 2 的幂) |
kernel_size |
nvhashmap 默认 | 内核大小 (1-1024) |
key_fetch_queue_length |
8 | 软件预取管道长度 {0,1,2,4,8} |
prefetch_values |
true | 是否发出软件预取指令 |
minimize_psl |
false | 是否缩短 probe search length |
auto_shrink |
false | 大量淘汰后是否自动缩容 |
overflow_policy |
— | 淘汰策略(EvictRandom / EvictLRU / EvictLFU) |
模板分发:produce() 在多维 switch 上分发:mask_size × key_size × overflow_handler × kernel_size × minimize_psl × auto_shrink × partitioner → ~6144 种编译时类型组合。
5.2 Abseil 插件(libnve-plugin-abseil.so)
| 属性 | 内容 |
|---|---|
| 注册名 | abseil_flat_map |
| 底层库 | Google absl::flat_hash_map<KeyType, char*>(Swiss Table) |
| 存储位置 | CPU 端内存 |
| 数据持久化 | ❌ 不持久 |
实现方式:继承 STLContainerTable,复用全部分片、内存槽管理、超额淘汰逻辑,底层哈希表替换为 absl::flat_hash_map。
链接依赖:absl_raw_logging_internal + absl_hash + absl_raw_hash_set。
5.3 PHMAP 插件(libnve-plugin-phmap.so)
| 属性 | 内容 |
|---|---|
| 注册名 | phmap_flat_map |
| 底层库 | greg7mdp parallel-hashmap phmap::flat_hash_map |
| 存储位置 | CPU 端内存 |
| 数据持久化 | ❌ 不持久 |
实现方式:与 Abseil 插件完全对称——继承 STLContainerTable,底层替换为 phmap::flat_hash_map。模板分发维度:mask_size × key_size × overflow_handler × partitioner → ~192 种类型组合。
5.4 Redis 插件(libnve-plugin-redis.so)
| 属性 | 内容 |
|---|---|
| 注册名 | redis_cluster |
| 底层库 | hiredis + redis++ — TCP 连接 Redis Cluster |
| 存储位置 | 远程 Redis 集群 |
| 数据持久化 | ✅ 持久化 |
核心配置:
| 字段 | 默认值 | 说明 |
|---|---|---|
address |
localhost:6379 |
Redis 集群任意节点地址 |
user_name |
default |
Redis 用户名 |
password |
空 | Redis 用户密码 |
connections_per_node |
5 | 与每个 Redis 节点的最大并行连接数 |
connection_lifetime |
180 秒 | 连接生命周期 |
max_batch_size |
16384 | 批量查询的最大 batch size |
链接依赖:hiredis + hiredis_ssl + redis++_static + ssl + crypto。
5.5 RocksDB 插件(libnve-plugin-rocksdb.so)
| 属性 | 内容 |
|---|---|
| 注册名 | rocksdb |
| 底层库 | Facebook RocksDB(LSM-Tree) |
| 存储位置 | 本地磁盘 |
| 数据持久化 | ✅ 持久化 |
核心配置:
| 字段 | 默认值 | 说明 |
|---|---|---|
column_family |
default |
RocksDB Column Family 名称 |
max_batch_size |
16384 | 批量操作的最大 batch size |
verify_checksums |
true | 是否校验 checksum |
path |
/tmp/rocksdb |
数据库文件系统路径 |
read_only |
false | 只读模式(允许多客户端并发读取) |
num_threads |
16 | RocksDB 实例可用线程数 |
六、内置降级实现(无插件)
即使不加载任何插件,也有 6 个别名指向同一个内置实现:
static std::unordered_map<std::string, create_host_table_factory_t> htf_impls{
{"stl_map", create_stl_map_table_factory},
{"map", create_stl_map_table_factory},
{"stl_unordered_map", create_stl_map_table_factory},
{"unordered_map", create_stl_map_table_factory},
{"stl_umap", create_stl_map_table_factory},
{"umap", create_stl_map_table_factory}
};
底层使用 std::unordered_map<KeyType, char*> + 手动管理的内存槽(slot buffer),带分片(shared_mutex)、inline meta(LRU/LFU 标记)、超额淘汰逻辑。
七、编译时特征控制
在 CMakeLists.txt 中定义:
set(NVE_AVAILABLE_FEATURES "")
list(APPEND NVE_AVAILABLE_FEATURES
"abseil_plugin" "nvhm_plugin" "phmap_plugin"
"redis_plugin" "rocksdb_plugin"
)
- 默认(
NVE_DEFAULT_FEATURES):启用全部 5 个插件 +ht_key_32+ht_kernel_128 - 最小(
NVE_MINIMAL_FEATURES):仅ht_mask_64 + ht_key_64 + ht_part_fnv1a + ht_kernel_64,无插件 - 可通过
-DNVE_DISABLE_PLUGINS=1或-DNVE_FEATURES="nvhm_plugin redis_plugin"定制
每个启用的 feature 转换为 C++ 编译宏 -DNVE_FEATURE_{NAME}=1,用于模板分发的条件编译。
八、JSON 运行时配置示例
{
"plugins": ["libnve-plugin-redis.so"],
"table_factories": {
"my_redis_factory": {
"implementation": "redis_cluster",
"address": "my-redis-cluster:6379",
"password": "****",
"connections_per_node": 10
},
"my_nvhm_factory": {
"implementation": "nvhm_map",
"key_size": 8,
"mask_size": 64,
"max_value_size": 8192,
"value_dtype": "float32",
"num_partitions": 4,
"overflow_policy": {
"handler": "EvictLRU",
"overflow_margin": 512,
"resolution_margin": 0.2
}
}
},
"tables": {
"0": "my_redis_factory",
"1": "my_nvhm_factory"
}
}
代码调用:
auto db = nve::build_host_database(json);
auto& table0 = db.at(0); // Redis 集群表
auto& table1 = db.at(1); // NVHM GPU 哈希表
九、总结
| 维度 | 说明 |
|---|---|
| 架构风格 | C 符号约定 + dlopen/dlsym 动态加载 + 抽象工厂模式 |
| 不是 | C++ 虚基类插件,而是 extern “C” 符号 + dlsym 解析 |
| 5 种存储后端 | GPU 端(NVHM)、CPU 端内存(Abseil/PHMAP)、远程(Redis)、持久化磁盘(RocksDB) |
| 内置降级 | std::unordered_map 实现无需任何插件即可运行 |
| 模板分发 | 多维 switch 分发到编译时特化模板类,最多 6144 种组合 |
| 构建/运行时解耦 | 编译时通过 NVE_FEATURES 选择构建内容,运行时通过 dlopen 按需加载 |