perf: reduce CPU usage during rapid typing#174
Merged
Conversation
- app.ts: skip onEvent() call when event is negligible (改善A) Move source/filter onEvent dispatch after the skip guard so it is not executed for redundant TextChangedI events where input has not actually changed. cbContext.revoke() is also moved to the correct position (after the skip guard, before onEvent). - context.ts: validate options only on mutation (改善B) Add #needsValidation flag to ContextBuilderImpl. All set*/patch* methods set the flag; createContext runs #validate() only when the flag is true, then clears it. This avoids iterating every option key on every keystroke. - ddc.ts: TextEncoder/TextDecoder module-level singletons (改善C) byteposToCharpos() and charposToBytepos() previously allocated new TextEncoder/TextDecoder instances on every call. Reuse a single pair of instances at module scope. - ddc.ts: fix cacheTimeout time source (改善G) Date#getSeconds() returns 0-59 and wraps, causing the minute boundary to reset the comparison and prevent cache expiry. Switch to Math.floor(Date.now()/1000) for a monotonically growing seconds-since-epoch value. - ext.ts: eliminate duplicate getFilter() calls (改善E) runMatchersConcurrently() resolved filters once for the parallelSafe check but then passed the original UserFilter names back to callFilters(), which resolved them again. Refactor to a callResolvedFilters() helper that operates on pre-resolved tuples and reuse those across all branches and chunks. Agent-Logs-Url: https://github.com/Shougo/ddc.vim/sessions/b543f2b0-5b9e-4f7c-84ce-a7e4942a8f2c Co-authored-by: Shougo <41495+Shougo@users.noreply.github.com>
- app.ts: add comment explaining why cbContext.revoke() sits after the skip guard - ext.ts: replace non-null assertion (!) with a type-predicate filter when narrowing resolved matchers, eliminating the unchecked cast Agent-Logs-Url: https://github.com/Shougo/ddc.vim/sessions/b543f2b0-5b9e-4f7c-84ce-a7e4942a8f2c Co-authored-by: Shougo <41495+Shougo@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
Shougo
April 25, 2026 10:41
View session
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
素早いキー入力時の CPU 使用率増加に対して、前回の調査結果から特定された5つのホットスポットを修正します。
Changes
改善A —
onEvent()を skip 判定の後へ移動 (denops/ddc/app.ts)TextChangedIが連続して発火しisNegligibleがtrueになるケース(入力が変化していない冗長なイベント)でも、従来は全ソース・全フィルターのonEvent()を呼んでいました。skipチェックの 後にonEvent()/cbContext.revoke()を移動することで、この無駄な呼び出しを排除します。cbContext.revoke()も skip ガードの後へ移動し(意味的に正しい位置)、コメントを追加しました。改善B — オプション変更時のみ
#validate()を実行 (denops/ddc/context.ts)createContext()は全キー入力ごとにDdcOptionsの全キーをループして検証していました。#needsValidationフラグを追加し、set*/patch*メソッド呼び出し後のみ検証が走るようにします。ランタイム中はオプションが頻繁に変わらないため、ほぼ定常的に検証をスキップできます。改善C — TextEncoder/TextDecoder をモジュールレベルシングルトンに (
denops/ddc/ddc.ts)byteposToCharpos()とcharposToBytepos()は呼び出しのたびにnew TextEncoder()/new TextDecoder()を生成していました。モジュールスコープの定数に変更することで GC 圧力を削減します。改善G —
cacheTimeoutの時刻ソースを修正 (denops/ddc/ddc.ts)Date#getSeconds()は 0–59 の値を返し、分をまたぐとリセットされます。そのため分境界でキャッシュが期限切れにならないバグがありました。Math.floor(Date.now() / 1000)(エポックからの秒数)に変更して単調増加する値を使用します。単位は従来通り「秒」のまま維持するため、既存のcacheTimeout設定値は変更不要です。改善E —
getFilter()の重複呼び出しを排除 (denops/ddc/ext.ts)runMatchersConcurrently()はparallelSafeチェックのためにフィルターを一度解決した後、callFilters()に元のUserFilter名を渡していたため、各チャンクごとに再度getFilter()が呼ばれていました。解決済みタプルを受け取るcallResolvedFilters()ヘルパーを導入し、全ブランチ・全チャンクで再利用します。missing filter のハンドリングは型述語フィルターで行い、非 null アサーション (!) を除去しました。セーフティチェック
InsertLeave/BufEnter等はisNegligibleがfalseになるためonEvent()は引き続き呼ばれます。TextChangedIの skip=true ケース(同一 input の連続イベント)でsource.onEventがスキップされますが、このケースでソースが副作用を期待することは通常ありません。set*/patch*で必ずフラグが立つため、設定変更直後の最初の補完サイクルでは検証が実行されます。cacheTimeoutの単位(秒)は変わらないため後方互換性あり。