Files
msime-windows/engine/core/pinyin_decoder.cpp
T
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

94 lines
2.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pinyin_decoder.h"
#include "data_path.h"
#include "../googlepinyinime-rev/src/include/pinyinime.h"
#include "../googlepinyinime-rev/src/include/lpicache.h"
#include <mutex>
#include <utf8.h>
namespace metasequoia
{
namespace
{
struct DecoderService
{
std::mutex mutex;
std::filesystem::path model;
std::filesystem::path user_dictionary;
bool ready = false;
std::size_t clients = 0;
~DecoderService()
{
ime_pinyin::im_close_decoder();
}
};
DecoderService &service()
{
static DecoderService instance;
return instance;
}
} // namespace
PinyinDecoder::PinyinDecoder(std::filesystem::path model, std::filesystem::path user_dictionary)
: model_(std::move(model)), user_dictionary_(std::move(user_dictionary))
{
auto &decoder = service();
std::lock_guard lock(decoder.mutex);
++decoder.clients;
}
PinyinDecoder::~PinyinDecoder()
{
auto &decoder = service();
std::lock_guard lock(decoder.mutex);
if (--decoder.clients == 0)
{
ime_pinyin::im_close_decoder();
decoder.ready = false;
decoder.model.clear();
decoder.user_dictionary.clear();
}
}
std::string PinyinDecoder::sentence(const std::string &pinyin) const
{
if (pinyin.empty() || pinyin.size() > 128 || model_.empty() || user_dictionary_.empty())
return {};
auto &decoder = service();
std::lock_guard lock(decoder.mutex);
if (decoder.model != model_ || decoder.user_dictionary != user_dictionary_)
{
ime_pinyin::im_close_decoder();
decoder.ready = false;
// The upstream lemma cache is also global and contains model-specific IDs.
auto &cache = ime_pinyin::LpiCache::get_instance();
for (ime_pinyin::uint16 id = 0; id < ime_pinyin::kFullSplIdStart; ++id)
cache.put_cache(id, nullptr, 0);
decoder.model = model_;
decoder.user_dictionary = user_dictionary_;
decoder.ready =
ime_pinyin::im_open_decoder(path_to_utf8(model_).c_str(), path_to_utf8(user_dictionary_).c_str());
if (!decoder.ready)
return {};
ime_pinyin::im_set_max_lens(128, 64);
}
if (!decoder.ready)
return {};
// 不先 im_reset_search:upstream 的 search 自己会与上一次的输入比公共前缀,从分歧处
// 回退再往后解,连续打字时每键只多解一个字母。前缀为零时它走的就是整段重置。
const auto count = ime_pinyin::im_search(pinyin.data(), pinyin.size());
for (std::size_t i = 0; i < count; ++i)
{
ime_pinyin::char16 buffer[256] = {};
if (!ime_pinyin::im_get_candidate(i, buffer, 255))
continue;
std::size_t length = 0;
while (length < 255 && buffer[length] != 0)
++length;
if (length)
return utf8::utf16to8(std::u16string(reinterpret_cast<const char16_t *>(buffer), length));
}
return {};
}
} // namespace metasequoia