Files
fanlusky e365a8d1b5 perf(engine): 大模型改为只读映射并按需载入,缩短每键查询路径
- sc.lm 改用 kenlm 的 LAZY 载入:Windows 上默认的 POPULATE_OR_READ 会退成 READ,
  把 33 MB 整份拷进私有堆且永不释放;现在是 MapViewOfFile 只读映射,可被系统回收
- dict_japanese.dat 在 Windows 上同样改为只读映射,与 POSIX 分支一致
- 神经整句模型改为开关首次生效时才载入(LazySentenceModel),默认关闭的桌面模型
  不再占用约 24 MB
- 新增 reset_sentence_cache():重排结果与上文只影响词格整句,只清 series 一层,
  逐前缀查库与切分缓存保持热;set_rescoring_context 也改用它
- CircularBuffer 新增 find():命中时返回指针而不复制整份候选列表,并改为真正的 LRU;
  全拼、双拼每键的命中路径改用它
- Google 解码器去掉每次调用前的 im_reset_search,沿用上游的公共前缀增量搜索
- 测试:出货 sc.lm 能映射载入并正常打分;增量搜索与从头搜结果逐步一致
2026-09-30 19:58:18 +08:00

91 lines
2.0 KiB
C++

#pragma once
#include <list>
#include <optional>
#include <unordered_map>
template <typename Key, typename Value> class CircularBuffer
{
public:
explicit CircularBuffer(size_t capacity) : _capacity(capacity)
{
}
void insert(const Key &key, const Value &value)
{
auto it = _map.find(key);
if (it != _map.end())
{
it->second.first = value;
return;
}
if (_list.size() >= _capacity)
{
const Key &oldest_key = _list.front();
_map.erase(oldest_key);
_list.pop_front();
}
_list.push_back(key);
_map[key] = {value, std::prev(_list.end())};
}
// 命中时把条目挪到最近使用端(真正的 LRU 淘汰),返回指向缓存值的指针而不复制。
// 候选列表一份就是几百个 WordItem,每键的热路径上应当用它而不是 get()。
// 指针在下一次 insert / remove / clear 之前有效。
const Value *find(const Key &key)
{
auto it = _map.find(key);
if (it == _map.end())
{
return nullptr;
}
_list.splice(_list.end(), _list, it->second.second);
return &it->second.first;
}
std::optional<Value> get(const Key &key) const
{
auto it = _map.find(key);
if (it != _map.end())
{
return it->second.first;
}
return std::nullopt;
}
bool remove(const Key &key)
{
auto it = _map.find(key);
if (it == _map.end())
return false;
_list.erase(it->second.second);
_map.erase(it);
return true;
}
void clear()
{
_map.clear();
_list.clear();
}
size_t size() const
{
return _map.size();
}
bool empty() const
{
return _map.empty();
}
private:
size_t _capacity;
std::list<Key> _list;
std::unordered_map<Key, std::pair<Value, typename std::list<Key>::iterator>> _map;
};