0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】検索をもっと速く、もっと滑らかに進化させた話(v0.7.1)

0
Posted at

はじめに

Pythonで開発しているタブ型・プレビュー付きのファイル検索アプリ「FileSearchExplorer」、前回の記事ではv0.6.0で詳細検索パネルや重複ファイル検索を追加した話をご紹介しました。

今回は機能追加ではなく、検索そのものの体感速度を底上げすることに全振りしたアップデート「v0.7.1」をリリースしました。見た目上の変化は少ないですが、中身はかなり手を入れています。

GitHubリポジトリはこちら:
https://github.com/Unknown777hello/FileSearchExplorer

v0.7.1での主な進化ポイント

大量の検索結果でもヌルヌル動く仮想スクロール

検索結果が数千件になると、これまでも一定件数を超えたら「今見えている分だけ」を描画する仮想スクロールで対応していたのですが、実は描画するバッファ件数が固定値で、PageUp/PageDownを押すと画面何ページ分も一気に飛んでしまったり、逆にウィンドウを小さくすると余計な件数まで描画してしまったりしていました。

v0.7.1では、Treeviewの実際の表示領域の高さから「今何行表示できているか」をそのつど計算し、それに応じてバッファ件数を動的に調整するようにしました。あわせて、

  • マウスホイールは1クリックあたり3行(Windowsの標準的な挙動に合わせる)
  • PageUp/PageDownは実際の1画面分だけ移動

という形にスクロールの粒度を整理し、ウィンドウのリサイズにも自動追従するようにしています。

検索処理をスレッド分割して並列化

これまでの検索は、指定フォルダをos.walkで頭から順番に舐めていく単一スレッドの処理でした。今回、フォルダ直下のサブフォルダ単位で走査スレッドを分割し、ThreadPoolExecutorで並列に走査するParallelSearchWorkerを新設しました。

ファイルの探索はディスクI/Oが主体の処理なので、Pythonの弱点としてよく言われるGIL(Global Interpreter Lock)があっても、I/O待ちの間は他のスレッドに処理が回るため、マルチコア環境では体感できるレベルで速くなります。サブフォルダが少ないフォルダや、非再帰検索(直下のみ)のときはスレッドプールを立てるだけ無駄なので、その場合は自動的に従来どおりの単一スレッド処理にフォールバックするようにしています。

SQLiteインデックス検索のさらなる高速化(FTS5導入)

インデックス検索はLIKE '%keyword%'でファイル名を絞り込んでいたのですが、この「先頭にワイルドカードが付くLIKE検索」は、実はどれだけインデックスを貼っても高速化できないというSQLiteの構造的な弱点があります。

そこで今回、SQLiteのFTS5拡張のtrigramトークナイザーを使った検索用の仮想テーブルを追加しました。ファイル名を3文字単位の断片(トライグラム)に分解してインデックス化することで、部分一致検索でもインデックスが効くようになり、大量ファイルを登録したフォルダでもキーワード検索がぐっと速くなっています。

古いバージョンのSQLiteなど、trigramトークナイザーが使えない環境では自動的に従来のLIKE検索にフォールバックするので、動作環境を問わず安全に使えるようにしています。あわせて、書き込み系のPRAGMA(synchronous=NORMALなど)やキャッシュサイズのチューニング、VACUUM後のPRAGMA optimize実行なども見直しました。

開発の裏話

今回一番気を遣ったのは、速くする代わりに検索結果が不正確になっては本末転倒という点でした。

FTS5のtrigram検索は既定で大文字小文字を区別しない挙動になるのですが、アプリ側には「大文字/小文字を無視」をOFFにするオプションがあります。そこで、FTS5はあくまで「候補を高速に絞り込むための一次フィルタ」として使い、実際にヒットとして表示するかどうかは、これまで通りのPython側の判定ロジックで必ず再チェックする、という2段構えの設計にしました。速度と正確さを両立させるための、地味だけど重要な安全弁です。

並列検索側でも似たような悩みがありました。複数スレッドが同時にフォルダを歩くので、ショートカットやジャンクションによる循環参照(無限ループ)を検知する「訪問済みフォルダ」の記録を、スレッド間でロックを取りながら共有する必要があります。ここを雑に作ると、シンボリックリンクの多い環境でスレッドごとに二重にファイルを処理してしまい、同じファイルが検索結果に2件表示される、なんてバグを一瞬踏みました。地味な排他制御ですが、並列処理を安全に成立させるための土台の部分だなと改めて実感しました。

最後に(WindowsならPython不要で動きます)

今回もPython環境がないPCでも、Windows環境(10/11 64bit)ならReleasesからzipをダウンロードするだけでそのまま単体で動きます。

見た目の変化は地味ですが、大量のファイルを扱うほど効果を実感しやすいアップデートになったと思います。

バグ報告や「こんな機能が欲しい!」といったフィードバックは、引き続きGitHubのIssuesでお待ちしています。ぜひスターもポチッとお願いします!

最新版のダウンロード(Releases):
https://github.com/Unknown777hello/FileSearchExplorer

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?