Git For Windows・Git SDK の起動を爆速にする
Git For Windows やその上位互換である Git SDK (以降「GitBash」で総称する) の起動時のトロさといったら。Mac のターミナルくらい爆速で起動して使い始めたいのに、git-bash.exe
を起動して最初のプロンプトが表示されるまで2・3秒待たないといけない。
今回はそんな Windows GitBash の起動を爆速にするための取り組みをまとめる。
目次
- Windows GitBash が遅い理由
- じゃあ、速くするにはどうしたらいいのか?
- Git SDK の前提環境
- GitBash 起動時に読み込まれているファイル・処理内容を知る
- Git SDK の起動を爆速にする
/etc/profile
- Git For Windows も高速化する
- Git For Windows の起動を爆速にする
/etc/profile
- どのくらい速くなったのか検証する
- 以上
- その他参考
Windows GitBash が遅い理由
そもそも何で GitBash は起動や動作が遅いのだろう。
最近気付いたのだが、MacOS 上で、VirtualBox で Windows 環境を作り (Modern.IE とかでいい)、その上で Git For Windows をインストールすると、仮想環境とは思えない速度でターミナルが起動するのだ。ということは、「MacOS の SSD が速くて、Windows マシンの SSD は遅いのかしら?」と、ハードウェアの性能差すら疑ってしまった。
で、そんな疑問を調べていたら、Twitter で素晴らしいリプライをいただいた。
Windowsのファイルアクセスとプロセス起動がUNIX系に比べて相当遅い傾向があるせいじゃないでしょうか。
ファイルアクセスが遅い問題はSSD使うとそれなりには緩和されると思います。
あとVirtualBoxのストレージコントローラには「ホストのI/Oキャッシュを使う」設定があり、これでも速くなりそうです— SODA Noriyuki (
@n_soda
) 2018年12月19日Windowsのファイルアクセスとプロセス起動がUNIX系に比べて相当遅い傾向があるせいじゃないでしょうか。
ファイルアクセスが遅い問題はSSD使うとそれなりには緩和されると思います。
あとVirtualBoxのストレージコントローラには「ホストのI/Oキャッシュを使う」設定があり、これでも速くなりそうです
薄々勘付いてはいたが、そもそも Windows OS 自体が、ファイルアクセスとプロセス起動が遅い傾向にあるようだ。
ファイルアクセスというと、test
コマンドなどでファイルの存在チェックを行って、source
コマンドで読み込んだり、といったことが起動時に行われている。原理的には、OS を問わず、ファイルアクセスの回数や量に比例して動作が遅くなっていくが、Windows OS ではそれを MacOS や Linux OS よりも顕著に感じる、ということだろう。
GitBash の中身はほぼ MSYS2 で、Bash コマンドを Windows 向けに移植して .exe
化して取り揃えている。Bash の言語仕様上の細かなところを知らないので推測にはなるが、平たく考えると、Bash コマンドを実行する度に、コマンドの実体である .exe
ファイルを読みに行き (ファイルアクセス)、そのプロセスを起動して、処理しているワケだ。起動時に使用するコマンド ≒ 処理が多ければ多いほど、Windows ではより顕著に速度性能を体感してしまう、というワケだ。
あと、コレは体感的な話ではあるが、echo
によるターミナルへの出力自体も、もっさりしているように感じる。Git SDK はターミナル起動時に「Welcome」とかなんちゃら echo
してきやがるので、コンソール出力も止めよう。
そうそう、Mac の VirtualBox 上で動作する Windows だとそのような遅さを感じなかった理由についても判明した。ホスト OS の I/O キャッシュが利用されているので、MacOS のファイルアクセスの性能が活かされているということのようだ。
じゃあ、速くするにはどうしたらいいのか?
Windows は「ファイルアクセス」と「プロセス起動」に時間がかかることが、GitBash の起動の遅さの原因と考えられることが分かった。
ということは、この裏を返せば、起動を速くする方法に繋がるだろう。
- ファイルアクセスを減らす
test
コマンドなどによるファイルの存在チェックを省けるだけ省くsource
コマンドで外部ファイルを読み込んでいる箇所を省く : 呼び出し元のスクリプトファイルにインラインでベタ書きしてやれば同じ結果が得られるので、インライン化してしまうmkdir
・chmod
などファイルを操作するコマンドを調整する
- プロセス起動を減らす
exec
や、存在チェックなどの条件分岐や関数呼び出し、変数展開や置換など、考えられる「無駄な処理」は削れるだけ削り、使うコマンドの数を減らす
とりあえず、起動時に読み込まれたり、参照・操作されるファイル数を減らし、余計な処理をガンガン削ったら、原理的には速くなりそうだ。
しかし、起動時に読み込まれているファイルって何なのだろう?
一つずつ調べていくことにした。
Git SDK の前提環境
まずは Git SDK の起動を速くしていこうと思う。前提となる環境は以下のとおり。
git-sdk-installer-1.0.7-64.7z.exe
(Git SDK v1.0.7) をC:\git-sdk-64\
にインストール後、1回以上普通に起動したことがある (初期設定が完了している)C:\git-sdk-64\
ディレクトリのgit log
は以下のとおり74eb921e 2018-12-18 Update 20181218-031256 [Git for Windows Build Agent] (grafted, HEAD -> master, origin/master)
C:\git-sdk-64\.git\
ディレクトリを消しておく。そうしないと、/
が Git 管理されているテイになっているので、Git 管理外のディレクトリでも__git_ps1
が表示されてしまう
このような状態で、調査を開始した。
GitBash 起動時に読み込まれているファイル・処理内容を知る
Git SDK (git-bash.exe
) を起動すると、現状は3秒くらい時間がかかっている。
まずは起動時にどのようなファイルが読み込まれているか調べるため、/usr/bin/bash.exe
の動作ログを出力してみる。やり方は以前別の記事で紹介した。
$ PS4='+$BASH_SOURCE> ' BASH_XTRACEFD=7 bash -xl 7>&2
bash.exe
が立ち上がると、C:\git-sdk-64\etc\profile
(= /etc/profile
) が読み込まれ、このファイルから次のファイル群が source
されていた。
/etc/msystem
:MSYSTEM_PREFIX
など環境変数を設定する/etc/post-install/
配下の全ファイル : インストール直後の初期設定がメイン。設定ができていたら結果的に何もしないのだが、Git SDK を起動するたびにsource
するので遅くなっているようだった/etc/post-install/01-devices.post
:/dev/
配下のシンボリックリンクを作成する/etc/post-install/03-mtab.post
:/proc/mounts
の再現/etc/post-install/06-windows-files.post
:C:\Windows\System32\drivers\etc\
との設定/etc/post-install/07-pacman-key.post
:pacman-key
の設定/etc/post-install/08-xml-catalog.post
:xmlcatalog
の用意/etc/post-install/99-post-install-cleanup.post
: 不要なファイルの削除
/etc/profile.d/
配下のファイル :perlbin.csh
以外が呼ばれる/etc/profile.d/aliases.sh
:alias
設定/etc/profile.d/bash_completion.sh
: Bash の複雑なタブ補完用ファイル/usr/share/bash-completion/bash_completion
を読み込む/etc/profile.d/bash_profile.sh
:~/.bash_profile
・~/.bashrc
などのファイルの存在チェック・なければ新規生成する/etc/profile.d/env.sh
: 環境変数PATH
の設定/etc/profile.d/git-prompt.sh
: 環境変数PS1
(プロンプト) の設定/etc/profile.d/git-sdk.sh
:sdk
関数の用意。Welcome to the Git for Windows SDK!
のメッセージはこのスクリプトが出力している/etc/profile.d/lang.sh
: 環境変数LANG
(言語) の設定/etc/profile.d/perlbin.sh
: Perl に関する環境変数PATH
の設定/etc/profile.d/tzset.sh
: 環境変数TZ
(タイムゾーン) の設定
/etc/bash.bashrc
: プロンプトの設定など- あとは
~/.bash_profile
および~/.bashrc
。普段ユーザ定義する部分だが、ココのカスタマイズは今回対象外とする
この /etc/profile
と、そこから読み込まれる外部ファイル群から「無駄」を省いていけば速くなるかもしれない。
- これらのファイル群は、MSYS2 系の様々な環境で汎用的に動作するように書かれているので、32 bit 版向けの条件分岐や処理が含まれていたりする
- SSH 接続時の調整など、自分の用途ではまず使わない場合向けの設定が含まれている
今回、起動を速くするためなら自分が使う環境に強く依存するようなカスタマイズでもいとわないことにした。
まずは source
しているファイルの中身を /etc/profile
内にコピペし、外部ファイルの読み込みを全てなくした。その状態で、条件分岐や変数の設定内容などを細かく見ていき、通っていない処理、通っていても実質的に何も変更していない無駄な処理をコメントアウトした。ココは地道に変数の定義前・定義直後で変数を echo
したりして調べていった。
この細かな調査記録は以下にコメント込みで記述したファイル全量があるので、気になる人は参照されたし。
optimize-git-for-windows/profile v1 外部ファイルをインライン化・不要箇所をコメントアウト at master · Neos21/optimize-git-for-windows · GitHub- 現在は削除
Git SDK の起動を爆速にする /etc/profile
こうして極限まで無駄を省いた結果、/etc/profile
の内容は以下のように収めることができた。
# Git SDK 向け /etc/profile 超削減版
MSYS2_PATH=/usr/local/bin:/usr/bin:/bin
MANPATH=/usr/local/man:/usr/share/man:/usr/man:/share/man
INFOPATH=/usr/local/info:/usr/share/info:/usr/info:/share/info
ORIGINAL_PATH="$PATH" # 元のソースでは変数の内容チェックなどがあったが省いた
# 後述するが Git For Windows とは設定内容が違うので注意
MSYSTEM=MSYS
MSYSTEM_PREFIX=/usr
MSYSTEM_CARCH=x86_64
MSYSTEM_CHOST=x86_64-pc-msys
CONFIG_SITE=/etc/config.site
PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/share/pkgconfig:/lib/pkgconfig
# 元のソースは /tmp ディレクトリの特定に色々処理していたが、全てベタ書きにした
ORIGINAL_TMP=/tmp
ORIGINAL_TEMP=/tmp
TMPDIR=/tmp
# 本来は "$(exec /usr/bin/hostname)" で代入していたところなのだが、ベタ書きにしてしまった。export する必要性がイマイチ分かっていない変数
HOSTNAME=Neos-Windows
# プロンプトは最小構成にしておく。自分は ~/.bash_profile で設定しているのでココでは余計なことはせずにシンプルにしておく
PS1='\n$ '
# locale コマンドの結果だけ書いてしまう
LANG=ja_JP.UTF-8
# tzset コマンドの結果だけ書いてしまう
TZ=Asia/Tokyo
# 環境変数 PATH への代入は数箇所に登場していたので、1箇所でまとめてやるようにした
PATH="$HOME/bin:$MSYS2_PATH:/opt/bin:$ORIGINAL_PATH:/usr/bin/vendor_perl:/usr/bin/core_perl"
# export コマンドの実行は1回だけにする
export PATH MANPATH INFOPATH PKG_CONFIG_PATH LANG TZ TMP TEMP TMPDIR HOSTNAME PS1 SHELL ORIGINAL_TMP ORIGINAL_TEMP ORIGINAL_PATH MSYSTEM MSYSTEM_PREFIX MSYSTEM_CARCH MSYSTEM_CHOST CONFIG_SITE
コメント・空行を省いて19行になった。コレ以外は、条件分岐に合致せず呼び出されていないコードだったり、実行されても何も変化がないコードだったりしたので、全て削った。
変数展開のコストがどれだけかかるのか分からないが、余計な処理コストがかかるのを避けるため、原則的にベタ書きに変更してしまった。
一応処理として通ってはいたので残したものの、起動後の使用箇所が分からずまだ削れそうな変数もある。
$ORIGINAL_TMP
・$ORIGINAL_TEMP
: 中身は/tmp
になっていたので、結局$TMP
や$TEMP
と同じじゃん、と。$TMPDIR
ももしかしたら要らなさそう$HOSTNAME
: プロンプトに使用する\h
では参照していないので、もしかしたらコレも要らないかも$PS1
:~/.bash_profile
で独自に設定しているし、別にココで用意してあげなくても良いかも
とはいえ、基本は変数への固定値代入で、コマンドらしいコマンドは export
1回だけに押さえられた。
C:\git-sdk-64\etc\profile
= /etc/profile
のファイルの内容を、上述の内容に差し替えれば高速化できる。
Git For Windows も高速化する
同じ要領で、通常の GitBash である Git For Windows も高速化してみよう。
C:\Program Files\Git\
に v2.20.1 をインストール後、1回以上普通に起動した状態から調査・改良を開始した/etc/profile
の内容は Git SDK のモノと全く同一だったので、$MSYSTEM
の初期値が'MSYS'
ではなく'MINGW64'
であることによる、一部の条件分岐の違い程度と推測した- が、念のため全体の動きを調べ直し、設定する環境変数の違いなどをまとめた
Git For Windows の起動を爆速にする /etc/profile
ということで出来上がった /etc/profile
は以下のとおり。
MSYS2_PATH=/usr/local/bin:/usr/bin:/bin
MANPATH=/usr/local/man:/usr/share/man:/usr/man:/share/man
INFOPATH=/usr/local/info:/usr/share/info:/usr/info:/share/info
ORIGINAL_PATH="$PATH"
# 以下の内容が Git SDK と異なる
MSYSTEM=MINGW64
MSYSTEM_PREFIX=/mingw64
MSYSTEM_CARCH=x86_64
MSYSTEM_CHOST=x86_64-w64-mingw32
MINGW_CHOST=x86_64-w64-mingw32
MINGW_PREFIX=/mingw64
MINGW_PACKAGE_PREFIX=mingw-w64-x86_64
CONFIG_SITE=/mingw64/etc/config.site
PKG_CONFIG_PATH=/mingw64/lib/pkgconfig:/mingw64/share/pkgconfig
ORIGINAL_TMP=/tmp
ORIGINAL_TEMP=/tmp
TMPDIR=/tmp
# やりすぎず "$(exec /usr/bin/hostname)" とした方が良いかも。もしくは不要?
HOSTNAME=Neos-Windows
# env.sh より以下を追加した
DISPLAY=needs-to-be-defined
SSH_ASKPASS=/mingw64/libexec/git-core/git-gui--askpass
PS1='\n$ '
LANG=ja_JP.UTF-8
# Git For Windows には tzset.sh がなかったので本来は以下の変数が設定されないが、Git SDK と同じ内容を設定しておく
TZ=Asia/Tokyo
PATH="$HOME/bin:$MSYS2_PATH:/opt/bin:$ORIGINAL_PATH:/usr/bin/vendor_perl:/usr/bin/core_perl"
# ACLOCAL_PATH にも値が最初から入っていたので export 対象に入れた
export PATH MANPATH INFOPATH PKG_CONFIG_PATH LANG TZ TMP TEMP TMPDIR HOSTNAME PS1 SHELL ORIGINAL_TMP ORIGINAL_TEMP ORIGINAL_PATH MSYSTEM MSYSTEM_PREFIX MSYSTEM_CARCH MSYSTEM_CHOST CONFIG_SITE ACLOCAL_PATH DISPLAY SSH_ASKPASS
コメント行、空行を除くと24行になった。
環境変数 $MSYSTEM
のデフォルト値が Git SDK と Git For Windows とで異なっていたので、その違いによる環境変数の差分を取り込んだ。32bit 版の Git For Windows を使っている人はまたちょっと変わってくるはず。
- 参考 : MSYS2でMinGW環境整備(3)
どのくらい速くなったのか検証する
さて、当初の体感では2・3秒起動時に待たされていたのが、上述の改修後は、起動して2秒後にはプロンプトが表示されているくらいに高速化された。
数値としてどのくらい高速化したのか、Git SDK 側の変更前後で違いを見てみる。
/etc/profile
の1行目と最終行、および ~/.bash_profile
の最終行 (~/.bashrc
の読込後) の3箇所に、以下のコマンドを仕掛けた。
date '+%F %T %N'
# 'YYYY-MM-DD HH:mm:ss ナノ秒' が出力される
この状態で git-bash.exe
を起動し、変更前の /etc/profile
と変更後の /etc/profile
とで比較してみた。
元々の /etc/profile
の実行結果
2018-12-20 22:04:54 425751900 # /etc/profile 開始
Welcome to the Git for Windows SDK!
The common tasks are automated via the `sdk` function;
See `sdk help` for details.
2018-12-20 22:04:56 071816800 # /etc/profile 終了
2018-12-20 22:04:56 648292200 # ~/.bash_profile 終了
sdk()
関数の welcome
の内容が出力されているので少し分かりづらいが、/etc/profile
の終了まで2秒近くかかっていることが分かる。
~/.bash_profile
の終了にも0.6秒程度かかっているが、コレはこのファイルから ~/.bashrc
や git-completion.bash
などを source
していたりするため。ユーザ定義の場所なので参考程度に見て欲しい。
変更後の /etc/profile
の実行結果
2018-12-20 22:32:09 508269500 # /etc/profile 開始
2018-12-20 22:32:09 548539700 # /etc/profile 終了
2018-12-20 22:32:09 983198600 # ~/.bash_profile 終了
2秒ほどかかっていた /etc/profile
の読み込みは、なんと0.04秒程度で完了するようになった。コメントアウトや空行の有無は実行速度に影響なかった。
こうなると ~/.bash_profile
・~/.bashrc
の読み込み速度の方が気になってくるレベル。それでも全体では0.5秒程度で起動スクリプトの実行が終わっているのは驚異的。
Git For Windows の方も同様だった。Git For Windows の方は /etc/post-install/
ディレクトリ自体が元々存在しなかったので、変更前から Git SDK よりも少し速かったのだが、それよりも格段に速くなった。
以上
体感できているとおり、GitBash の起動を爆速にすることができた。
/etc/profile
の変更内容を見てもらえば分かるとおり、自分の環境で git-bash.exe
を起動した時に上手く動きさえすれば良い、というノリで滅茶苦茶にコードを削っている。/etc/profile
の内容は別環境には共用できないし、まだ遭遇していないだけで思いもよらぬ不具合が起こるかもしれない。ご利用は計画的に。
今回の改修および調査結果は、全て以下のリポジトリに置いているので、GitBash の起動を爆速化してみたい人は、参考にしてほしい。