性能とリソースの計測のしかた¶
扱うこと: perf.yml による手動計測(取り込み・インデックス作成・検索・入力しながらの検索の速さとリソース)、手元の Windows での取り込み・入力しながらの検索の計測、取り込みの計測が頼る「計測の口」。扱わないこと: 速さの回帰テスト(速さの回帰テストと上限の決め方)。先に読むページ: CI。
perf.yml(性能とリソースの計測)¶
flowchart LR
W["gh workflow run perf.yml<br>ref・scale・count・ingest・typing"] --> DATA["tebunko-perfdata の new_index.ps1<br>でデータを生成"]
DATA --> ING["Office からの取り込み<br>(ingest ≠ 0 のときだけ)"]
DATA --> IDX["インデックス作成<br>publishIndexFolders"]
DATA --> SEARCH["検索<br>語ごとに新しいプロセス"]
DATA --> TYPING["入力しながらの検索<br>(typing ≠ 0 のときだけ。語ごとに新しいプロセス)"]
ING & IDX & SEARCH & TYPING --> RES["リソースの記録<br>(0.2 秒ごと)"]
RES --> OUT["Summary(表・Mermaid)<br>result.json・metrics.csv"]
手動で起動し、決まった量のデータで、Office からの取り込み、インデックス作成、検索の速さ、およびその間のリソース(メモリ・CPU・スレッド・ハンドル・GC)を測る。大きな変更の前後で数字を比べるときなどに使う。必須のチェックではない。
gh workflow run perf.yml -f ref=<測る ref> -f scale=0.1 -f ingest=200
- 入力:
ref(測る ref。作業中のブランチも測れる)、scale(データの量。0.04はブック約 2,100・TSV 約 6,400(入力しながらの検索の 7,000 件規模)、1でブック 5.4 万・TSV 16 万・約 1GB)、count(語ごとに続けて検索する回数。既定 20)、ingest(取り込みを測る .docx・.pptx のそれぞれの数。02001000。既定0は取り込みを測らない。検索だけを測る起動が長くならないようにする)、xlsx(Excel のブックを ZIP から読む部分を測る、普通のブックを読む回数。05。既定0は測らない)、typing(入力しながらの検索を測る、語ごとの入力の繰り返し回数。01020。既定0は測らない)、typing_min_length(入力しながらの検索で最初に送る語の長さ。3(既定。画面と同じで合否を決める)・1(短い語の取り消しを多く含む厳しめの参考値)) - 計測スクリプト(
tools/measure_perf.ps1とtools/perf/)はワークフローの ref から、計測対象のコードは入力の ref からチェックアウトする。計測スクリプトが呼ぶ関数(publishIndexFolders・getContentIndexFiles・searchContentIndex。名前を改める前の版のgetIndexPackFiles・searchPackIndexでもよい)が無い ref(本文インデックスの形式より前の版)では、エラーメッセージを出して止まる - データは tebunko-perfdata の
new_index.ps1で毎回生成する(取り込みの一時置き場と同じ形の TSV)。使う版はperf.ymlのPERFDATA_SHAでコミットに固定する。検索する語は同じリポジトリのwords.tsv(0 件・まれ(3 件)・大量・正規表現) - 測るもの(それぞれ別のプロセスで動かし、リソースが混ざらないようにする)
- Office からの取り込み …
ingestが0でないときだけ。tools/perf/new_ingest_data.ps1でテストデータ(tests/testdata/office)の複製を作り(.docx・.pptx をingest個ずつ。50 ファイルごとにフォルダを分ける)、measure_perf.ps1 -Officeで測る。ランナーには Office が無いので、Office を使わずに読むファイル(.docx・.pptx。読み取りのスレッド)だけを測り、Excel・Word・PowerPoint を使う取り込み(.xlsx・.doc・.ppt)は手元の Windows で測る(下の「取り込みの計測」) - Excel の流れ読み …
xlsxが0でないときだけ。tools/perf/measure_xlsx.ps1を起動し、ブックをその場で ZIP から組み立てて(tools/perf/xlsx_data.ps1。Office は使わず、テストデータにも置かない)、readXlsxObjectUnits(図形・コメント・ヘッダー/フッターを読む。シートの流れ読みを含む)の時間を測る。シート 1 枚の普通のブックをxlsx回読んだ時間(最小・中央値・平均・最大)と、同じシートの部品を指す<sheet>を 200 個並べたブックを 1 回読んだ時間を出す。直す前と後の ref を、それぞれ同じ形で測って比べる(metrics.csvのxlsx_normal_ms・xlsx_many_ms) - インデックス作成 … インデクサと同じ
publishIndexFolders(本文インデックスを書く・TSV を消す)。Office からの取り込みは、下の取り込みの計測で別に測る。画面ではインデックス作成のスレッドの優先度を下げている(BelowNormal)が、計測ではほかに動くものが無いので、優先度は下げずに測る - 検索 … 語ごとに新しいプロセスを起動し、同じプロセスで
count回続けて検索する(上限 1 万件)。画面と同じく、検索の司令のスレッド(newSearchService)を 1 つ作り、要求(newSearchRequest)を順に送る。lib.ps1の読み込みと照合のスレッドの用意は司令のスレッドが始めに 1 回だけ行うので、1 回目の時間に含まれる。検索のキャッシュも画面と同じくプロセスで 1 つを持ち続ける。語ごとに、検索時間の最小・中央値・平均・最大と、その内訳(列挙・照合)を出す。1 回目は画面を開き直した直後の検索に当たり、たいてい最大の値になる- 司令のスレッドが無い版(#102 より前)は、その版の画面と同じく、検索のたびに新しい Runspace で
lib.ps1を読み込んで測る(内訳に読み込みの時間も出す)。どちらで測ったかは結果のSearchMode(service/runspace)に出るので、#102 の前後を同じワークフローで比べられる
- 司令のスレッドが無い版(#102 より前)は、その版の画面と同じく、検索のたびに新しい Runspace で
- 入力しながらの検索 …
typingが0でないときだけ。語ごとに新しいプロセス(tools/perf/measure_typing.ps1)を起動し、語をtyping_min_length文字目から 1 文字ずつ伸ばした語(画面のインクリメンタルサーチと同じ)を、150 ms おきに検索の司令のスレッド(newSearchService)へ送る(新しい要求は前の要求を取り消す)。これを 1 回の入力としてtyping回繰り返す。測るのは、最後の要求を渡した時点(渡す時間を含む)から、前の要求がすべて終わるまで・最初のヒットまで・検索が終わるまでの時間。1 回目は画面を開き直した直後の入力に当たるため、統計(最小・中央値・平均・最大)は 2 回目以降から作る。時刻は Windows のタイマーの細かさの分、大きく出る側に最大で約 16 ms の誤差がある。画面はヒットを 100 ms ごとに取り込むため、画面に出るまでは最大で約 100 ms 長くなる。検索の司令のスレッドが無い版(#102 より前)は測らない(結果のTyping.Modeがnoneになる。失敗にはしない) - 選択行のプレビュー …
perf.ymlでは測らない。measure_perf.ps1 -Preview <件数>で、検索で見つかったヒットの先頭から件数分を、結果の表を↓キーで移るのと同じ順に、検索のキャッシュ無し・ありの両方でプレビューする(語ごとに新しいプロセスtools/perf/measure_preview.ps1。ヒットの無い語は飛ばす)。1 回ごとの読み込み(画面の最初の読みと同じ呼び方のreadContentIndexLines。前に読んだファイルの控えを次の読みに渡す)と表づくり(HitRow.BuildPreview)の時間・作ったセルの数・メモリをpreview.csvに、語ごとの統計と、GC のあとに残るメモリ・検索のキャッシュを捨てた分をresult.jsonのPreview・metrics.csv・summary.mdの「プレビュー」に出す。-Render(-Previewと一緒に指定。STA で動く)は、いちばん大きな表を画面の部品に入れて Measure・Arrange する時間も 5 回測り(描画は、残るメモリを取ったあとに行うので、残るメモリには入らない)、1 回目(部品の初期化が入る)と 2 回目以降の中央値・最大、行・列・セルの数をPreview.Words[].Render(FirstMs・RestMs。形式の版 4 で追加)に出す。表を作る時間も、最初の 1 回と 2 回目以降に分けてBuildFirstMs・BuildRestMs(版 4)に出す。見えている範囲のセルだけを作る今の表のために、表の行がそのとき持っているセルの数(CreatedCells。見える範囲を作ったあとの数)と、描画の部品の数・右と下へスクロールして範囲を作り直す時間・描画のあとに残るメモリ(RenderのElements・ScrollMs・RenderRetainedMB)も出す(版 5)。RenderRetainedMBは、表を持ったまま描画を繰り返し、GC をしたあとのマネージドヒープの、描画の前からの増え(表と部品を保持している間の値。手放したあとの値ではない)。-ReadMore(-Previewと一緒に指定)は、セルの数が最大だった行の表へ下に 200 行ずつ最大 5 回読み足す時間(読む・行を作る・見える範囲のセルを作る)をPreview.Words[].ReadMore(版 5)に出す。-PreviewFullRead(-Previewと一緒に指定)は、控えを捨てて毎回ファイルの全体を読む(初めて開くファイルの最初の読みに当たる)読み方でも、別のプロセスで同じ選択を測り、Preview.Words[].FullRead(形式の版 3 で追加)に今の読み方と同じ形で並べる。切り替えが効いたかは、readContentIndexTextが呼ばれた回数(ReadTextCallsUncached)で確かめ、呼ばれていなければ失敗にする。perf-check.ymlのsearchジョブが上限と比べる(速さの回帰テストと上限の決め方) - リソース … 別のスレッドで 0.2 秒ごと(および段階の切り替わり)に、プロセスのワーキングセット・プライベート・マネージドヒープ、CPU 時間、スレッド・ハンドルの数、GC の回数と、PC 全体の CPU・空きメモリを記録する。検索では、検索 1 回ごとのメモリも記録し、10 回あたりの増え方(最小二乗の傾き)を出す(検索を続けたときにメモリが増え続けないかを見るため)
- OS のファイルキャッシュは空にしない。本文インデックスは作成の直後なので OS のキャッシュに載っており、PC を起動した直後の検索(ディスクから読む)とは違う
- 結果は、ジョブの Summary(表と Mermaid のグラフ)と、artifact
perf-<実行の番号>(保存期間は 90 日)に出力する。パスやファイル名は出力しない summary.md… Summary と同じ内容(「Excel の流れ読み」「Office からの取り込み」「入力しながらの検索」の節を含む)result.json… すべての数字。形式の版(Schema)と実行の情報(実行の番号・ref・コミット・scale・日時・ランナーの CPU)を含むmetrics.csv… 1 行 1 指標の縦長の形(run_id, date, ref, sha, scale, metric, word, stat, value, unit)。実行をまたいで数字を貯め、Grafana などで見るときに使う(貯める仕組みはまだ無い)searches.csv(検索 1 回ごと)・typing.csv(入力しながらの検索、1 回の入力ごと)・resource-xlsx.csv・resource-ingest.csv・resource-index.csv・resource-search.csv・resource-typing.csv(リソースの記録)- ランナー(4 コア)は手元の PC と条件が違う。Defender のリアルタイム保護の状態は、結果の「環境」に出力する。比べるときは、同じワークフローで測った数字どうしで比べる。同じ条件でも、本文インデックスの作成の時間は 5 割ほどぶれることがある
- 手元でも同じ計測スクリプトで測れる(
.\tools\measure_perf.ps1 -Index <TSV のフォルダ> -Work <作業フォルダ> -Words <words.tsv> -Typing <入力の回数>)。渡した TSV は本文インデックスに変換され、元の TSV は削除される
取り込みの計測(手元の Windows。Excel・Word・PowerPoint が入った機械で)¶
.\tools\perf\new_ingest_data.ps1 -Dest C:\perf\office -Docx 50 -Pptx 50
.\tools\measure_perf.ps1 -Office C:\perf\office -Work C:\perf\work -Repeat 1
- データ(
new_ingest_data.ps1)… .docx・.pptx(-Docx・-Pptx。既定 200)、.doc・.ppt(-Doc・-Ppt。既定 0)はテストデータの複製、.xlsx(-Xlsx。既定 0)は tebunko-perfdata のnew_books.ps1で作ったブック(-Books)の先頭の数冊。50 ファイルごとにフォルダを分ける(フォルダごとの本文インデックスの書き出しも一緒に測るため)。同じ引数からは同じ構成・同じ中身になる。Excel は .xlsx、Word は .doc、PowerPoint は .ppt を読むので、使う Office はデータにある種類で決まる - 測り方(
tools/perf/measure_ingest.ps1)…-Repeat回(既定 3)、1 回ごとに新しいプロセス・空のワークスペースで流す(Office の起動を含む、初めての取り込みの時間)。測る tebunko のscriptsを作業フォルダに写してsetting.configを書くので、利用者の設定・既定のワークスペース・リポジトリのsetting.configには触らない。取り込みは起動口indexer.ps1 -Channelで動かし、記録のスレッドが受け渡しの口のProgress.Phaseを読んで、段階(クロール・確認・取り込み・仕上げ)ごとの時間とリソースを出す。1 ファイルあたりの ms は、取り込みの段階の秒 ÷ ファイル数 - 成功・失敗は、ワークスペースの
store\ingest_status.tsvを計測の側で読んで数える(同じ相対パスは最後の行の状態)。成功 + 失敗がファイル数と合わなければ、終了コードが 0 でなければ、取り込みの段階が読めなかったとき・知らない段階の名前が来たときは、計測を失敗にする。失敗したファイルがあればsummary.mdに書く - リソースの表が測るのは、計測の PowerShell のプロセスだけ。EXCEL・WINWORD・POWERPNT のプロセスは含まない(「PC の CPU」には含む)
- Office を使う取り込み(.xlsx・.doc・.ppt)の時間に固定の上限を付けた合否のテストは無い。Office の時間は機械・Defender・Office の版で大きく揺れるので、比べるのは同じ機械・同じ日・同じ引数で続けて測った数字どうしにする(Office の版は結果の実行の情報に出す)。比べ方は下の「Office を使う形式の比べ方」。Office を使わずに読む .docx・.pptx は、
perf-check.ymlが固定の上限と比べる
計測の口(取り込みの計測が頼るもの)¶
取り込みのコードを書き直すときは、この口を変えない。変えるときは、先に計測(measure_ingest.ps1)を直し、変える前の版を測り直してから比べる。
| 口 | 中身 |
|---|---|
| 起動口 | scripts\tebunko\indexer.ps1 -Channel <口>。終了コード 0 = 完了 |
| 読み込み口 | scripts\tebunko\lib.ps1(resolveTebunkoLib が頼っている)の newIndexerChannel(引数 retryFailed・confirmTargets・workers) |
| Excel の流れ読み | scripts\tebunko\indexer\indexer_lib.ps1(lib.ps1 も読み込む)が読み込む readXlsxObjectUnits(引数 path・failures・sizeFailures)。無い版は xlsx を渡すと失敗する |
| 受け渡しの口 | Progress.Phase と、その値 クロール・確認・取り込み・仕上げ |
| 設定 | setting.config がツールのフォルダにあること。キー targetFolders(@{ name; path; enabled } の配列)・workspaceFolder・ingestThreads |
| 取り込み一覧 | ワークスペースの store\ingest_status.tsv。見出しの 相対パス・状態。状態の値 済・失敗 |
| 取り込んだ結果 | 最後の回のワークスペース(<作業フォルダ>\ingest\ws。measure_ingest.ps1 は次の回の始めまで消さない)の下に、本文インデックスのファイル content_index.*.tsv(インデックスのファイルの形の形。サブフォルダの下にもできる)があること。取り込みの回帰テスト(perf_ingest.Tests.ps1)が、数と合計の大きさを数える |