diff --git a/memo/ChangeLog.md b/memo/ChangeLog.md index 85c306f9..72b56825 100644 --- a/memo/ChangeLog.md +++ b/memo/ChangeLog.md @@ -183,6 +183,7 @@ - history: use `mapfile -d ''` to load history in Bash 5.2 `#D1603` 72c274e - prompt: use `${PS1@P}` when the prompt contains only safe prompt sequences `#D1617` 8b5da08 - prompt: fix not properly set `$?` in `${PS1@P}` evaluation (reported by nihilismus) `#D1644` 521aff9 +- decode: cache `inputrc` translations `#D1652` 0000000 ## Compatibility diff --git a/note.txt b/note.txt index 745f0ae6..17c67c0e 100644 --- a/note.txt +++ b/note.txt @@ -5372,6 +5372,119 @@ bash_tips Done (実装ログ) ------------------------------------------------------------------------------- +2021-09-22 + + * decode: 巨大 inputrc の翻訳内容をキャッシュする [#D1652] + https://github.com/Bash-it/bash-it/pull/1884#issuecomment-923489130 + + /etc/inputrc{,.keys} が大量に設定を行っている為に初期化が物凄く遅い問題に + ついて。これは bash の既定の binding に対する cmap cache だけではなくて、 + 前回の ble.sh 実行時の cmap cache も保持する事で解決する様な気もする。と + いうか寧ろ前回の ble.sh 実行時の状態を使って cmap cache を保持するべきの + 気もする。 + + [現状] + + 現在どの様にキャッシュしているか確認する。 + + 例えば decode.readline.50108.vi-insert.txt 等に対して保存されている。これ + に対してユーザーが保存した物も付け加えるのはどうだろうか。と思ったが + emacs, vi-insert, vi-command で分けて保存しているのはどういう事だろうか。 + うーん。emacs/vi-insert/vi-command の三組で ble.sh の keybinding の状態が + 定まる。という事を思うと、ユーザーの inputrc の状態を保存するとしてもやは + り三組を記録する必要がある。そして、それらの保存されたファイルと現在の状 + 態に相違がない時に限り保存された keybinding の状態を復元するという振る舞 + いにする。 + + --noinputrc で分岐している部分を確認する。noinputrc が指定されている時に + は以下の変数に値を設定している。これらの変数に値が設定されていない時に限 + り現在の状態が読み取られる。 + + _ble_builtin_bind_inputrc_done=noinputrc + + これは ble/builtin/bind/initialize-inputrc で参照される。ユーザーの + inputrc を読み取る関数。 + + _ble_builtin_bind_user_settings_loaded=noinputrc + + これは現在の bind -vetc の出力を元に keybinding を設定する関数 + ble/builtin/bind/read-user-settings 及び + ble/builtin/bind/.reconstruct-user-settings で処理される。 + + うーん。この .reconstruct-user-settings の中で呼び出している各ステップで + どれだけ時間がかかっているのかを計測するのが先ずはする事の気がする。もし + かすると bottleneck を見誤っているかもしれない。 + + 実際に時間を計測してみると、比較対象と現在の状態の両方を gawk に入力する + 為に集めるので 0.04s かかっている。gawk は 0.016s で終わっている。その後 + の ble-bind で 0.77s 消費している。 + + [修正] + + * ok: 入力情報を集める部分については恐らく設定の量に依存しないし、入力情報 + が前回と一致しているかどうかというのを判定するのにまた時間がかかるだろう + から、キャッシュの同一性の判定は gawk で処理した後に行うべきの気がする。 + + * done: 後段の最も時間がかかっている部分について内容を確認する。bind -m xxx + 'xxx' というのが約200行続いている。これについてキャッシュできないか考える + べきなのだろう。この時点での内容を何処かに保存しておいて… + + % うーん。そもそも初期化の順序がどうなっているのか分からない。この関数の + % 呼び出しが初期化時の物であれば、この時点での keymap の状態は標準の状態 + % になっていると考えられるので、"$settings" の内容だけでこれを eval した + % 後の状態が確定するのでキャッシュを読み込んで終わりにする事ができる。一 + % 方で、別の場所から呼び出された時には、更に別の設定が keymap に加えられ + % ている可能性もあるので、不用意にキャッシュする事はできない。 + % + % →という事を考えると初期化時の呼び出しの時だけキャッシュする様に、呼び + % 出し元から特別な opts を指定する等して区別しなければならない。 + % + % もう一つの事はユーザー設定をキャッシュする為には emacs.sh 及び vi.sh を + % 読み込まなければならないという事である。そうすると keymap の遅延読み込 + % みが全く為されなくなり意味がない。 + % + % * うーん。bind の遅延には対応していたのだったか。もし bind の遅延に対応 + % していたとすると更に話はややこしくなる。遅延した物を最後にひとつにま + % とめてその上でキャッシュするという様な仕組みにする必要が出てくる。然 + % し、それは複雑すぎる様に思われる。 + % + % →うーん。遅延されている。という事を考えると 0.7s かかっているのは実 + % 際の ble-bind ではなくて、それをキャッシュする段階でかかっている時間 + % という事になる。 + % + % というかその前にそもそも遅延がどの段階で実装されているのかを確認する + % 必要がある→うーん。内部関数の ble-decode-key/bind の呼び出しがキャッ + % シュされている。この関数は decode 済みの keys 値と実際のコマンド名 + % (ble/widget/xxxx など) を受け取っている。という事を考えると、時間がか + % かっているのは decode の部分なのだろう。 + % + % * 元の標準設定が切り替わった時にはどうするのか。その時にもキャッシュを + % 更新しなければならない。 + + どうも時間がかかっているのは bind -m xxx yyy の yyy に含まれる keyseq + -> keys への翻訳の様に思われる。keymap は最初は初期化されていないと思わ + れるので、翻訳結果は全て ble-decode-key/bind の中でキャッシュされる。そ + のキャッシュ結果を何処かに保存しておけば良いという事になるのではないか。 + + 翻訳過程をキャッシュしているので cmap 及び rlfunc 表の更新だけを気にし + ていればキャッシュの一貫性は保てる。 + + 確認: 本当に初回呼び出しでは全ての keymap が未登録状態か? →実際にそうなっ + ている事を確認した。その条件が満たされている時にのみキャッシュを利用する + 事にする。 + + 取り敢えず実装した。動作確認した。inputrc を編集した時にちゃんと更新され + る事も確認した。 + + * done: 情報を集める箇所で ble/util/cat を3回実行している。これは readfile + か何かに置き換えるべきなのではないか。 + →readfile に置き換えた。 + + * done: というか reconstruct のパイプを assign で分解したら 0.12s から + 0.06s に短くなった。パイプは分解した方が良いのか…。 + + →分解した。 + 2021-09-21 * edit: set-mark 及び history-search-{for,back}ward を nmap で bind しようとしている [#D1651] diff --git a/src/decode.sh b/src/decode.sh index 2f586ba0..2102c9b1 100644 --- a/src/decode.sh +++ b/src/decode.sh @@ -3739,8 +3739,8 @@ function ble/builtin/bind/initialize-inputrc { # user 設定の読み込み _ble_builtin_bind_user_settings_loaded= -function ble/builtin/bind/.reconstruct-user-settings { - local map q=\' +function ble/builtin/bind/read-user-settings/.collect { + local map for map in vi-insert vi-command emacs; do local cache=$_ble_base_cache/decode.readline.$_ble_bash.$map.txt if ! [[ -s $cache && $cache -nt $_ble_base/ble.sh ]]; then @@ -3748,11 +3748,13 @@ function ble/builtin/bind/.reconstruct-user-settings { LC_ALL= LC_CTYPE=C ble/bin/sed '/^#/d;s/"\\M-/"\\e/' >| $cache.part && ble/bin/mv "$cache.part" "$cache" || continue fi + local cache_content + ble/util/readfile cache_content "$cache" ble/util/print __CLEAR__ ble/util/print KEYMAP="$map" ble/util/print __BIND0__ - ble/bin/cat "$cache" + ble/util/print "${cache_content%$'\n'}" if ((_ble_bash>=40300)); then ble/util/print __BINDX__ builtin bind -m "$map" -X @@ -3762,7 +3764,12 @@ function ble/builtin/bind/.reconstruct-user-settings { ble/util/print __BINDP__ builtin bind -m "$map" -p ble/util/print __PRINT__ - done | LC_ALL= LC_CTYPE=C ble/bin/awk -v q="$q" -v _ble_bash="$_ble_bash" ' + done +} +function ble/builtin/bind/.reconstruct-user-settings { + local collect q=\' + ble/util/assign collect ble/builtin/bind/read-user-settings/.collect + <<< "$collect" LC_ALL= LC_CTYPE=C ble/bin/awk -v q="$q" -v _ble_bash="$_ble_bash" ' function keymap_register(key, val, type) { if (!haskey[key]) { keys[nkey++] = key; @@ -3847,6 +3854,55 @@ function ble/builtin/bind/.reconstruct-user-settings { } ' 2>/dev/null # suppress LC_ALL error messages } + +## @fn ble/builtin/bind/read-user-settings/.cache-enabled +## @var[in] delay_prefix +function ble/builtin/bind/read-user-settings/.cache-enabled { + local keymap use_cache=1 + for keymap in emacs vi_imap vi_nmap; do + ble/decode/keymap#registered "$keymap" && return 1 + [[ -s $delay_prefix.$keymap ]] && return 1 + done + return 0 +} +## @fn ble/builtin/bind/read-user-settings/.cache-alive +## @var[in] settings +## @var[in] cache_prefix +function ble/builtin/bind/read-user-settings/.cache-alive { + [[ -e $cache_prefix.settings ]] || return 1 + [[ $cache_prefix.settings -nt $_ble_base/lib/init-cmap.sh ]] || return 1 + local keymap + for keymap in emacs vi_imap vi_nmap; do + [[ $cache_prefix.settings -nt $_ble_base/core-decode.$cache-rlfunc.txt ]] || return 1 + done + local content + ble/util/readfile content "$cache_prefix.settings" + [[ ${content%$'\n'} == "$settings" ]] +} +## @fn ble/builtin/bind/read-user-settings/.cache-save +## @var[in] delay_prefix +## @var[in] cache_prefix +function ble/builtin/bind/read-user-settings/.cache-save { + local keymap content + for keymap in emacs vi_imap vi_nmap; do + if [[ -s $delay_prefix.$keymap ]]; then + ble/util/copyfile "$delay_prefix.$keymap" "$cache_prefix.$keymap" + else + : >| "$cache_prefix.$keymap" + fi + done + ble/util/print "$settings" >| "$cache_prefix.settings" +} +## @fn ble/builtin/bind/read-user-settings/.cache-load +## @var[in] delay_prefix +## @var[in] cache_prefix +function ble/builtin/bind/read-user-settings/.cache-load { + local keymap + for keymap in emacs vi_imap vi_nmap; do + ble/util/copyfile "$cache_prefix.$keymap" "$delay_prefix.$keymap" + done +} + function ble/builtin/bind/read-user-settings { if [[ $_ble_decode_bind_state == none ]]; then [[ $_ble_builtin_bind_user_settings_loaded ]] && return 0 @@ -3854,7 +3910,20 @@ function ble/builtin/bind/read-user-settings { builtin bind # inputrc を読ませる local settings ble/util/assign settings ble/builtin/bind/.reconstruct-user-settings - builtin eval -- "$settings" + [[ $settings ]] || return 0 + + local cache_prefix=$_ble_base_cache/decode.inputrc.$_ble_decode_kbd_ver.$TERM + local delay_prefix=$_ble_base_run/$$.bind.delay + if ble/builtin/bind/read-user-settings/.cache-enabled; then + if ble/builtin/bind/read-user-settings/.cache-alive; then + ble/builtin/bind/read-user-settings/.cache-load + else + builtin eval -- "$settings" + ble/builtin/bind/read-user-settings/.cache-save + fi + else + builtin eval -- "$settings" + fi fi } diff --git a/src/util.sh b/src/util.sh index 5d098f31..a70c1368 100644 --- a/src/util.sh +++ b/src/util.sh @@ -2048,9 +2048,10 @@ function ble/builtin/trap/install-hook { ## if ((_ble_bash>=40000)); then function ble/util/readfile { # 155ms for man bash - local __buffer - mapfile __buffer < "$2" - IFS= builtin eval "$1=\"\${__buffer[*]-}\"" + local -a _ble_local_buffer=() + mapfile _ble_local_buffer < "$2"; local _ble_local_ext=$? + IFS= builtin eval "$1=\"\${_ble_local_buffer[*]-}\"" + return "$_ble_local_ext" } function ble/util/mapfile { mapfile -t "$1" @@ -2070,6 +2071,12 @@ else } fi +function ble/util/copyfile { + local src=$1 dst=$2 content + ble/util/readfile content "$1" || return $? + ble/util/put "$content" >| "$dst" +} + ## @fn ble/util/writearray [OPTIONS] arr ## 配列の内容を読み出し可能な形式で出力します。 ##