Skip to content

[WIP] Add converter result cache for performance improvement#156

Draft
Copilot wants to merge 1 commit intomainfrom
copilot/add-converter-result-cache
Draft

[WIP] Add converter result cache for performance improvement#156
Copilot wants to merge 1 commit intomainfrom
copilot/add-converter-result-cache

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 10, 2026

  • Explore codebase structure and understand converter pipeline
  • Create denops/ddu/convert_cache.ts with ConverterCache class (LRU+TTL) and singleton export
  • Modify denops/ddu/types.ts to add converterCache, converterCacheMaxEntries, converterCacheTTL to DduOptions
  • Modify denops/ddu/context.ts to add defaults in defaultDduOptions()
  • Modify denops/ddu/ddu.ts to integrate cache into converter execution path with makeConverterCacheKey and cache hit/miss logic
  • Run lint/type-check to verify changes
  • Code review
Original prompt

目的

Shougo/ddu.vim のパフォーマンス改善のため、core 側に汎用的な converter 結果キャッシュを追加します。COW(Copy-On-Write)や converters の一部で深い clone が発生するケースがあり、特に converter_hl_dir のような重い converter がボトルネックになっているため、converter の結果(DduItem 相当)をキャッシュして再利用することで総コストを削減します。

要件

  • キャッシュはオプトイン(DduOptions.converterCache: boolean、デフォルト false)。
  • キャッシュ実装は LRU + TTL(maxEntries と TTL(ms))で自動エビクションする。手動クリア / stats API は core 内に提供する(Vim 側ラッパは追加しない)。
  • キャッシュ値は DduItem を structuredClone して保存し、復元時は保存した DduItem の列挙可能プロパティを現行 item にコピーして適用する(従来の converter が item を mutate する前提を維持)。
  • Deno 環境を前提に structuredClone を使用し、JSON フォールバックは用いない。
  • 汎用的に使えるよう、すべての converter の入口で利用可能として統合する(特定 converter に依存しない)。
  • デフォルト無効。PR 説明に使い方・測定手順・注意点を記載する。

変更ファイル(追加/修正一覧)

  1. 新規ファイル: denops/ddu/convert_cache.ts
  • ConverterCache クラス(LRU + TTL)と singleton export converterCache を追加。
  • public メソッド: get/set/clear/stats/reconfigure。
  1. 修正: denops/ddu/types.ts
  • DduOptions に以下のフィールドを追加:
    • converterCache?: boolean
    • converterCacheMaxEntries?: number
    • converterCacheTTL?: number // ms
  • defaultDduOptions() にデフォルト値を追加(converterCache: false, converterCacheMaxEntries: 1000, converterCacheTTL: 60000)。
  1. 修正: denops/ddu/ddu.ts
  • converters 実行パスの入口に汎用キャッシュを統合。
    • cache キー生成関数 makeConverterCacheKey(item: DduItem): string | null を追加(item.path || item.word を基本 ID、存在すれば item.action?.mtime などを付与)。
    • キャッシュヒット時は cached DduItem を現行 item にマージ(列挙可能プロパティをコピー)して converters 実行をスキップ。
    • converter 実行後は structuredClone(item) をキャッシュに保存(this.#options.converterCache が true の場合)。
  • インスタンス初期化時に this.#options.converterCache が有効なら converterCache.reconfigure(...) を呼ぶ。
  • キャッシュ適用失敗時はグレースフルにフォールバックして通常の converter 実行を行う。
  1. ドキュメント(PR 本文)
  • 変更理由(ベンチ結果の要約)、使い方(オプトイン設定の例)、テスト手順(N 回の実行で median/mean 比較)、注意点(structuredClone 前提、将来的な per-converter serialize の余地、メモリ管理方針)を記載する。

実装詳細(技術的注意)

  • キー設計: makeConverterCacheKey はまず item.path || item.word を使う。さらに item.action?.mtime や item.action?.version があれば付加してファイル更新時の自動無効化を図る。
  • 保存データ: structuredClone(item) をそのまま保存して、再適用時は for (const k of Object.keys(cached)) item[k] = cached[k]; を行う。これにより converter が item を直接 mutate する既存の設計と互換性を保つ。
  • エビクション: Map を LRU キューとして利用し、insert 時にサイズ超過で先頭を削除する方式を採用。加えて TTL を保存時刻で判定する。
  • 設定: converterCacheMaxEntries(既定 1000), converterCacheTTL(既定 60000 ms)を DduOptions で指定可能。
  • 計測: converterCache.stats() を用いて hits/misses/evicted/size を取得可能。PR では profile ではなく、必要なら一時的にログ出力して確認する手順を記載する。

テスト手順(PR 本文に含める)

  1. デフォルト(converterCache: false)で N=5 回ベンチ(profile: true)を取得して baseline を取る。2. converterCache: true(maxEntries=1000, TTL=60000���で N=5 回ベンチを取り比較する。3. 比較指標: converter の ms(median/mean/total)、Refresh 全体時間、converterCache.stats() の hits/misses/evicted。4. TTL・maxEntries を調整して最適なパラメータを探る。

後続改善案(PR に追記)

  • per-converter の serialize/apply を導入してキャッシュサイズと再適用速度を改善する(将来)。
  • ファイル変更イベントで該当キーを削除するフックを追加するとより堅牢。

備考

  • Vim 側のラッパ関数は作らない(ユーザはオプションで切り替え)。
  • structuredClone がある Deno 前提の実装とする。

変更を反映する PR タイトル
"feat: add core-side generic converter result cache (LRU+TTL, opt-in)"

作業内容

  • 上記差分を branch にコミットし、PR を作成する。PR 本文に上記の説明、テスト手順、注意点を含める。

リポジトリ
Shougo/ddu.vim

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: copyOnWrite オフ

['Load file_rec: 40 ms', '[" matcher_substring: 1 ms items: 1005 -> 1005 inputChanged: false retType: array cloneDelta: 0"," matcher_hidden: 3 ms items: 1005
\ -> 980 inputChanged: false retType: array cloneDelta: 0"," sorter_mtime: 56 ms items: 980 -> 980 inputChanged: false retType: array cloneDelta: 0"]', '[" c
\onverter_hl_dir: 50 ms items: 980 -> 980 inputChanged: false retType: array cloneDelta: 0"]', '[" matcher_substring: 0 ms items: 1860 -> 1860 inputChanged: f
\alse retType: array cloneDelta: 0"," matcher_hidden: 6 ms items: 1860 -> 1811 inputChanged: false retType: array cloneDelta: 0"," sorter_mtime: 48 ms items:
\ 1811 -> 1811 inputChanged: false retType: array cloneDelta: 0"]', '[" converter_hl_dir: 65 ms items: 1811 -> 1811 inputChanged: false retType: array cloneDe
\lta: 0"]', 'Refresh all items: 1305 ms', '[" matcher_substring: 0 ms items: 1860 -> 1860 inputChanged: false retType: array cloneDelta: 0"," matcher_hidden:
\ 3 ms items: 1860 -> 1811 inputChanged: false retType: array cloneDelta: 0"," sorter_mtime: 5 ms items: 1811 -> 1811 inputChanged: false retType: array clone
\Delta: 0"]', '[" converter_hl_dir: 60 ms items: 1811 -> 1811 inputChanged: false retType: array cloneDelta: 0"]', 'Refresh all items: 1595 ms', '[" matcher_
\substring: 10 ms items: 1860 -> 1000 inputChanged: false retType: array cloneDelta: 0"," matcher_hidden: 3 ms items: 1000 -> 979 inputChanged: false retType:
\ array cloneDelta: 0"," sorter_mtime: 3 ms items: 979 -> 979 inputChanged: false retType: array cloneDelta: 0"]', '[" converter_hl_dir: 34 ms items: 979 ->
\979 inputChanged: false retType: array cloneDelta: 0"]', 'Refresh all items: 1878 ms', '[" matcher_substring: 6 ms items: 1860 -> 361 inputChanged: false ret
\Type: array cloneDelta: 0"," matcher_hidden: 0 ms items: 361 -> 35...

This pull request was created from Copilot chat.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants