str_replace, preg_replaceのパフォーマンス検証と呼び出しの最適化

概要

PHPでは文字列を置換するのにstr_replaceとpreg_replaceという関数を用いることができます、今回はそれぞれのパフォーマンスについて考察していきたいと思います。

予想としては当然preg_replaceのほうがコストが高く付くと思います、実際本当にそうなのか。またどの程度のパフォーマンスの開きが出るのかをいくつかのサンプルを用意して比較します。
簡単な表現から、多少複雑な表現まで検証してみます。
またそれぞれの関数の使用時の最適化についても調べたいと思います。

ちなみに当然ですがPHPの 公式マニュアル では特別な必要性がない限りstr_replaceを使用することを推奨しています。

preg_replaceとstr_replaceを理解する

preg_replaceとstr_replaceについてはご存じの方も多いかと思いますが、その名のごとくある文字列を対象として、特定の単語を別の単語で置き換えることのできる関数になります。
その違いも簡単で、str_replaceは特定の単語を検索置換するのに対して、preg_replaceは正規表現を用いて検索置換が行えます。

str_replaceについて簡単な例を示します。

$before = "this is test.";
$after = str_replace("this", "that", $before);
var_dump($after); // "that is test."

シンプルですね。文字列の置換ができます。

つづいてpreg_replaceについても簡単な例を示します。

$before = "this costs 1000yen. it costs 2000yen.";
$after = preg_replace("/(\d+)yen/", "¥$1", $before);
var_dump($after); // "this costs ¥1000. it costs ¥2000."

こちらも単純に正規表現を用いての置換ができることがわかりました。シンプルです。

性能を検証する

さて気になるパフォーマンスを見て行きましょう。
今回はテストケースを3つ用意しました。
1. 単純なパターンの単数の置換
2. 単純なパターンの複数の置換
3. 複雑なパターンの単数の置換

検証したPHPのバージョンは下記になります

$ php -v
PHP 5.4.30 (cli) (built: Jul 29 2014 23:43:29)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies

単純なパターンの単数の置換を実行した際のパフォーマンス

ここでは単純なパターンでの一つの文字列の置換を実行します。
用意する計測するコードは下記のようになります。

<?php
if (!isset($argv[1])) {
  echo 'usage -- php profile.php {try_count}' . PHP_EOL;
}
$count = intval($argv[1]);
echo "run test {$count} times." . PHP_EOL;

$subject = "this is test sentence one. this is test sentence two. this is test sentence three. this is test sentence four. this is test sentence five. this is test sentence six. this is test sentence seven. this is test sentence eight. this is test sentence nine. this is test sentence ten.";
echo "check performance of str_replace." . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = str_replace('this', 'that', $subject);
}
$end = microtime(true);
var_dump($end - $start);

echo "check performance of preg_replace" . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = preg_replace('/this/', 'that', $subject);
}
$end = microtime(true);
var_dump($end - $start);

計測します。

$ php profile_test_2.php 100000
run test 100000 times.
check performance of str_replace.
double(0.15461802482605)
check performance of preg_replace
double(0.35560202598572)

このような単純なパターンでも約二倍程度の性能差があることが見て取れました。
予想通りの結果となり、検索置換をアプリケーションで行うようであれば特別な理由なくpreg_replaceを使用するべきでないですね。

単純なパターンの複数の置換を実行した際のパフォーマンス

続いて複数文字列を置換する際のパフォーマンスについて検証します。
下記のようにサンプルプログラムを用意します。

<?php
if (!isset($argv[1])) {
  echo 'usage -- php profile.php {try_count}' . PHP_EOL;
}
$count = intval($argv[1]);
echo "run test {$count} times." . PHP_EOL;

$subject = "this is test sentence one. this is test sentence two. this is test sentence three. this is test sentence four. this is test sentence five. this is test sentence six. this is test sentence seven. this is test sentence eight. this is test sentence nine. this is test sentence ten.";
$source = array('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'nine', 'ten');
$dest = array(1,2,3,4,5,6,7,8,9,10);
echo "check performance of str_replace." . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = str_replace($source, $dest, $subject);
}
$end = microtime(true);
var_dump($end - $start);


$source = array('/one/', '/two/', '/three/', '/four/', '/five/', '/six/', '/seven/', '/nine/', '/ten/');
$dest = array(1,2,3,4,5,6,7,8,9,10);
echo "check performance of preg_replace" . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = preg_replace($source, $dest, $subject);
}
$end = microtime(true);
var_dump($end - $start);

計測します。

$ php profile_test_1.php 100000
run test 100000 times.
check performance of str_replace.
double(0.53684210777283)
check performance of preg_replace
double(1.4594769477844)

ここでも2倍から3倍程度の性能差が確認できました。
置換対象が複数になることでその性能差が開いていることが確認できます。

ここでちょっと気になるのは、str_replace, preg_replaceのそれぞれの性能を先の検証パターンと比較した時に性能が線形に悪化しているわけではなさそうだということです。
これについては別件として最後に検証を行うこととします。

複雑なパターンの単数の置換を実行した際のパフォーマンス

最後に複雑な正規表現を用いた際のパフォーマンスの違いについて見てみます。
下記のプログラムのように検索する表現を無駄に複雑にしてみます。

<?php
if (!isset($argv[1])) {
  echo 'usage -- php profile.php {try_count}' . PHP_EOL;
}
$count = intval($argv[1]);
echo "run test {$count} times." . PHP_EOL;

$subject = "this is test sentence one. this is test sentence two. this is test sentence three. this is test sentence four. this is test sentence five. this is test sentence six. this is test sentence seven. this is test sentence eight. this is test sentence nine. this is test sentence ten.";
echo "check performance of str_replace." . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = str_replace('this', 'that', $subject);
}
$end = microtime(true);
var_dump($end - $start);

$subject = "this is test sentence one. this is test sentence two. this is test sentence three. this is test sentence four. this is test sentence five. this is test sentence six. this is test sentence seven. this is test sentence eight. this is test sentence nine. this is test sentence ten.";
echo "check performance of preg_replace" . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = preg_replace('/([a-z]+) (is) (test)/', '$1 $2 $3', $subject);
}
$end = microtime(true);
var_dump($end - $start);

計測します。

$ php profile_test_3.php 100000
run test 100000 times.
check performance of str_replace.
double(0.14943814277649)
check performance of preg_replace
double(2.3758080005646)

すさまじい差ですね。(こんなケースはないかと思いますが)
正規表現による検索が、検索パターンによってコストが増大していることがわかります。

(番外編)置換対象が複数ある場合の関数呼び出し時の最適化について

検証パターン2の考察時にも触れましたが、複数の検索置換を行うときにstr_replace, preg_replaceは同様のインタフェースを用意しており、検索パターンと置換パターンを配列で渡すことができます。
複数の置換を実行するときに、置換を一回一回実行する方法と、置換パターンを配列で渡し一度に実行する方法の実装の仕方によってどれだけ差が出るかということを検証します。

プログラムを用意します。
一回一回実行する方法では、置換を行い、置換後の文字列に対して再度置換を行い、、、を繰り返します。

<?php
if (!isset($argv[1])) {
  echo 'usage -- php profile.php {try_count}' . PHP_EOL;
}
$count = intval($argv[1]);
echo "run test {$count} times." . PHP_EOL;

$subject = "this is test sentence one. this is test sentence two. this is test sentence three. this is test sentence four. this is test sentence five. this is test sentence six. this is test sentence seven. this is test sentence eight. this is test sentence nine. this is test sentence ten.";
echo "check performance of multiple call." . PHP_EOL;
$start = microtime(true);
for($i=0; $i<$count; $i++) {
  $subject = str_replace('one', 1, $subject);
  $subject = str_replace('two', 2, $subject);
  $subject = str_replace('three', 3, $subject);
  $subject = str_replace('four', 4, $subject);
  $subject = str_replace('five', 5, $subject);
  $subject = str_replace('six', 6, $subject);
  $subject = str_replace('seven', 7, $subject);
  $subject = str_replace('eight', 8, $subject);
  $subject = str_replace('nine', 9, $subject);
  $subject = str_replace('ten', 10, $subject);
}
$end = microtime(true);
var_dump($end - $start);

$subject = "this is test sentence one. this is test sentence two. this is test sentence three. this is test sentence four. this is test sentence five. this is test sentence six. this is test sentence seven. this is test sentence eight. this is test sentence nine. this is test sentence ten.";
echo "check performance of single call." . PHP_EOL;
$start = microtime(true);
$source = array('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten');
$dest = array(1,2,3,4,5,6,7,8,9,10);
for($i=0; $i<$count; $i++) {
  $subject = str_replace($source, $dest, $subject);
}
$end = microtime(true);
var_dump($end - $start);

計測してみます。

 $ php profile_test_4.php 100000
run test 100000 times.
check performance of multiple call.
double(1.2376821041107)
check performance of single call.
double(0.51983189582825)

!!!
をを。思った以上に差が出てきた
約倍程度のパフォーマンスの差がでています。

と余談なんですが、他のPCでも同じプログラムの実行を行ったところ、上記の結果ほどの差は出なかった。
その際のバージョンは5.4.30であった。

$ php profile_test_4.php 100000
run test 100000 times.
check performance of multiple call.
float(0.44886112213135)
check performance of single call.
float(0.36096215248108)

なんだかしこり残るのでphpのバージョンを5.6にあげて検証してみます。

$ php -v
PHP 5.6.6 (cli) (built: Feb 20 2015 22:35:31)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
...
$ php profile_test_4.php 100000
run test 100000 times.
check performance of multiple call.
double(1.22190117836)
check performance of single call.
double(0.46425199508667)

悪化した!?!?!?

ここから考察できるのはどうやらphp5.4.30以降str_replaceの性能は悪くなっているようである。

どちらにしても最適化された呼び出しとしては配列で一度に渡したほうが高速である。

以上、もろもろパフォーマンスについて検証しました、参考にしていただければと思います。


yumとかリポジトリとかとりまとめ

yumコマンドを用いてよく新しいモジュールをインストールすることって多いと思います。
このコマンド、あまり細部まで理解していなくても検索して出てきたコマンドをそのまま実行してだまってyをタイプしておけば大体の場合動くと思います。

ここで「よし動いた大丈夫」って思っている方は多分この記事を見る必要はないと思います。

実際内部でどういうことが起こっているのか、レポジトリってなに?(epel?remi?)なぜいっぱいあるの?
とか結構疑問はいっぱい出てきます。

今日はその辺のyumを取り巻く環境について調べたいと思います。

yumコマンド概要を理解する

yumとはYellowdog Updater Modifiedの略であり(それはどうでもいいか)パッケージを管理するメタパッケージ管理システムである。
FedoraやCentOSなどRPMベースのディストリビューションの多くでよく利用されている。
Wikipediaから抜粋

レポジトリとは動作の確認できているコンパイル済みのバイナリファイルを集めたストレージのことで、自分でわざわざソースコードの入手・コンパイルなどを行わなくても簡単に導入できます。
またソフトウェアが必要としているソフトウェアの依存性も自動的に検出して、必要な物も一緒にインストールしてくれます。

レポジトリを理解する

CentOSでよくみるレポジトリepelやらremiについて調べましょう。

Red Hat Enterprise Linux (RHEL)向けのパッケージであり、RHELから分岐したディストリビューション(CentOSもここに含まれます)と互換性のあるパッケージです。
目的はFedoraで提供されているパッケージをRHEL系のディストリビューションでも互換性を提供することにあるようです。
Fedoraで提供されている高品質なパッケージをそのまま互換性をのと同時に、プロジェクトに関してもFedoraプロジェクトと同じガイドライン、ルール、ポリシーに従うことを順守しているようです。

またremiについても同様に、有志によって管理されているプロジェクトになります。
公式ページを参照するとremiの目的ですが、最新バージョンのPHPモジュールをFedoraやRHEL系のディストリビューションに提供することとあります。それぞれ目的が異なるんですね。

yumコマンドを理解する

yumでのよく使うコマンドの一例を紹介します。

コマンド 動作
install パッケージをインストールする
update パッケージを更新する
check-update アップデート可能なパッケージ一覧を表示する
remove パッケージをアンインストールする
list インストール可能なパッケージ一覧を表示する
search 指定したキーワードでパッケージを検索する
info パッケージの情報を表示する

リポジトリの追加の仕方を理解する

remiリポジトリを例に順をおって説明していきます。

  • rpmファイルをダウンロードする
  • ネットで検索したりしてremiレポジトリのrpmファイルをダウンロードします。rpmファイルのパスは頻繁に更新されるようなので注意が必要です。

    wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
    

    ここでrpmファイルとは。これまた(RPM Package Manager)とよばれ、Redhatにより開発されたこれまたパッケージ管理システムになります。
    yumは実際にはrpmのラッパーとして動作しており、実際にパッケージのインストールに関してはrpmにより行われます。
    rpmはパッケージをcpio形式で圧縮し、その中にspecファイル・バイナリまたはソースコードが含まれます。またパッケージを管理するためにBerkeley DBを採用していて、これによりローカルのパッケージ情報の管理がなされる。
    specファイルにはパッケージの名前や、概要、依存するパッケージ、バイナリパッケージのインストールパス、インストール前後に実行するスクリプトなどが書かれていて、これによってユーザが意識しなくてもインストールを円滑に行うことができます。

    rpmでは依存するパッケージは提供されますが、その実行はユーザに任せています。この辺りを自動的に解決するのがyumということになります。

  • rpmファイルをインストールする
  • rpm --upgrade --verbose --hash remi-release-6.rpm
    

    インストールされたかどうかを確認するためには下記のコマンドを利用します。

    # rpm -qa | grep remi-release-6
    remi-release-6.5-1.el6.remi.noarch
    

    実際にリポジトリを使用するにはenablerepoと明示的にオプションに指定することや、設定ファイルに優先順位などを記述することで可能になります。
    ここでは試しにremiリポジトリが提供しているphpに関するパッケージをかくにしてみましょう。

    # yum --enablerepo=remi list | grep php
    php.x86_64                                 5.4.32-1.el6.remi            @remi
    php-cli.x86_64                             5.4.32-1.el6.remi            @remi
    php-common.x86_64                          5.4.32-1.el6.remi            @remi
    php-mysql.x86_64                           5.4.32-1.el6.remi            @remi
    php-pdo.x86_64                             5.4.32-1.el6.remi            @remi
    ...
    

    結局内部で何をしているかを理解する(つもりだったがまたの機会に)

    大雑把なことわかったんですが、中身が気になりますので追っていきます。
    確認環境はCentOS6.6になります。

    vi /usr/bin/yum
    --
    #!/usr/bin/python
    import sys
    try:
        import yum
    except ImportError:
        print >> sys.stderr, """\
    There was a problem importing one of the Python modules
    required to run yum. The error leading to this problem was:
    
       %s
    
    Please install a package which provides this module, or
    verify that the module is installed correctly.
    
    It's possible that the above module doesn't match the
    current version of Python, which is:
    %s
    
    If you cannot solve this problem yourself, please go to
    the yum faq at:
      http://yum.baseurl.org/wiki/Faq
    
    """ % (sys.exc_value, sys.version)
        sys.exit(1)
    
    sys.path.insert(0, '/usr/share/yum-cli')
    try:
        import yummain
        yummain.user_main(sys.argv[1:], exit_code=True)
    except KeyboardInterrupt, e:
        print >> sys.stderr, "\n\nExiting on user cancel."
        sys.exit(1)
    

    python。
    ちなみにですがpythonは結構コマンドラインと親和性が高いように思っています。
    デプロイツールの一種にもpythonを拡張したfabricというライブラリがあり、コマンドラインなんかを用意に実行できます。

    そしてyumスクリプトを見ると実態はpythonのラッパーとして動作していることがわかります。

    この続きについてはまた機会があるときに追跡しようと思います。

    本日はyumの周りをざっくり理解してもらえたでしょうか。


    vim tips その4

    前回にひき続いてvimのtipsを紹介していきましょう。
    今回はより快適に作業するためにvimが編集作業をどのように扱うのかをバッファ・ファイルについてと、より効率的に作業するためにウインドウやタブの概念について説明します。

    バッファを理解する

    vimがメモリ上に保持して展開している内容のことをバッファと呼びます。
    vimで何かしらのファイルをオープンした時、ファイルの内容をバッファに読み込みます。
    編集を行っているときは実際にはバッファの内容をオンメモリで編集しており、リアルタイムにファイルに書きだされているわけではないのです。したがって当然バッファの編集を行った場合、バッファの内容とファイルの内容には差がでます。
    当然最終的には編集したバッファの内容をファイルに書き出すことで、作業した内容の永続化処理をおなうことになります。

    つまりウインドウから我々ユーザが見ている内容はvimのバッファの内容ということになります。
    感覚的にはウインドウはバッファを除くビューポートのような働きをしていることになります。

    試しにvimから複数のファイルパスを引数に与えて起動してみましょう。

    vim a.txt b.txt
    

    するとvimは内部でそれぞれのファイルに対応するバッファを展開します。
    この様子は下記のexコマンドを実行することで、展開しているバッファの様子を確認することができます。
    (exコマンドには前回説明しましたので、わからない方は参照してみてください。)

    :ls
    

    exコマンドを実行すると下記のように表示されます。

    :ls
      1 %a   "a.txt"                        line 1
      2      "b.txt"                        line 0
    Press ENTER or type command to continue
    

    1,2と各バッファに項番がふられている様子と、それぞれのバッファに対応しているファイルパスが確認できます。
    またaactiveを表し、現在参照しているバッファを指しています。

    このようにvimではバッファを複数持つことができるので毎回ファイルを開き直さなくても編集可能なようになっています。
    続いて各種バッファに対する操作についても見ていきます。

    バッファを移動する

    各バッファについてはexコマンドを用いて移動することができます。それぞれについて説明します。

    exコマンド 意味
    :bnext 次のバッファに移動する
    :bprev 前のバッファに移動する
    :bfirst 最初のバッファに移動する
    :blast 最後のバッファに移動する
    :buffer{N} N番目のバッファに移動する
    :buffer {bufname} 指定の名称のバッファに移動する

    バッファを操作する

    展開しているバッファすべてに対してexコマンドを適用するようなこともでき、:bufdoで実現することができます。
    一気にファイルを操作することができて非常に便利です。

    またバッファは当然削除することもできます。
    ここに関してはバッファ自体が削除されるだけで、実際に永続化されているファイルも削除されるわけではありません。

    exコマンド 意味
    :bufdo 展開しているバッファ全てに対してexコマンドを実行する
    :bdelete {n} {m} {l} 指定した項番のバッファを削除する
    :{n},{m} bdelete 指定した項番の間にあるバッファを全て削除する

    引数リストを理解する

    バッファリストと少し似たような概念として引数リストがあり、これは名前が示すように、vimが展開している引数のリストになります。

    現在の引数リストを表示するには下記のように実行します。

    :args
    

    先ほどと同様にvimにオープンするファイルを引数にあげて起動した状態から:argsコマンドを叩くとどのようになるかを確認します。
    結果としては下記のように、引数として与えたファイルが列挙される形で表示されます。
    カギカッコでくくられているファイルは現在アクティブである引数を表しています。

    [a.txt] b.txt
    

    引数リストに関して覚えておくべきことは、それが常に同じ状態を持っていることではないこと、それと引数リストを設定することができることです。
    引数リストを設定するというのは実際には、exコマンドを用いて新たにバッファを追加するという事になります。

    引数リストの設定については下記のようになります

    :args {arglist}
    

    上記のような性質から当然、引数リストの内容は常に変更可能であり、:argsの出力結果がバッファリストの内容と一致するという保証はないです。
    次に引数リストを用いてバッファをオープンする方法を見ましょう。

    余談ですが、引数リストに関してはviの機能で、バッファリストに関してはvimで拡張された機能になります。
    なので似たような振る舞いを保つ機能が2つ存在しているかのようになっているのですが、引数リストを応用することで便利なことができます。

    引数リストを用いてバッファを操作する

    先に述べたように引数リストを指定することでバッファを新たに展開することができます。
    これの便利なところは指定する表現にglobを使用することができる点です。

    たとえば特定のフォルダ配下のすべてのファイルをバッファに展開したい場合は下記のようになります。

    :args **/*
    

    知っている方にはおなじみで直感的にわかりやすいと思います。
    また当然glob表現であるため、その表現を利用できます。

    例えばtxtファイルだけをバッファに展開したい場合は下記のようになります。

    :args **/*.txt
    

    また:argsコマンドはバッククォートを展開することができるので、例えばプロジェクトのファイルリストを保持しているファイルがある場合、下記のようなコマンドを用いてプロジェクトのファイルを引数リストに展開することも可能になります。

    :args <code>cat filelist</code>
    

    また先にすべてのバッファに対してexコマンドを適用するコマンド:bufdoを取り上げましたが、引数リストに関しても同様に引数リストに存在するもの全てに対してexコマンドを適用するコマンド:argdoが存在します。

    この2つに関して言えることはバッファリストに関しては、展開しているすべてのバッファを含んでしまうことになりますが、引数リストに関しては作業状況に応じて変更することが可能なものです。
    引数リストはテンポラリな作業場のようにして使うことができるのです。

    隠しバッファを理解する

    隠しバッファとは一言で言うと、編集中(ダーティー)なバッファのことです。
    あるファイルを展開して、それを編集した後に、保存処理などを行わないで終了しようとすると警告がでます。
    これを意図的に無視することができ、編集したものを一時的においておき、別のバッファに映るときに隠しバッファを利用することができます。

    例えば、あるバッファを編集し、それを隠しバッファとして次のバッファに映るときには下記のようにタイプします

    :bnext!
    

    !マークをつけることで隠しバッファとすることができます。

    これが何に利用できるのかということなんですが、展開している全バッファに対して一気に操作を適用するときに利用します。(というかこれしないと:bufdoなどが使えないです)

    vimには隠しバッファを利用するかしないかの設定をグローバルに保持しており、それは下記のコマンドで有効にすることができます。

    :set hidden
    

    :bufdoなどの複数バッファにコマンドを展開するコマンドの共同に関して隠しバッファを利用しない場合、下記のようなフローになります。

    1. 先頭のバッファの内容に変更を適用する
    2. ユーザに対してインタラクティブに保存を求める
    3. 次のバッファに移動する

    なのですが項目2.で:bufdoが止まってしまうため使用できないのです。

    よって隠しバッファを有効にすることでダーティな状態を許容することができ、項目2.がなくなり:bufdoが適用可能になります。

    ウインドウを理解する

    vimにおけるウインドウはバッファを参照するビューポートのような働きをします。
    もちろんウインドウを複数作成することができるのですが、そのウインドウが覗き見ているバッファの内容はなんでも良いです。
    これによって異なるウインドウから同じバッファを見ることなども可能になります。

    ウインドウの操作について見て行きましょう。

    ウインドウの分割

    コマンド 意味
    Ctrl-w s ウインドウを水平に分割する
    Ctrl-w v ウインドウを垂直に分割する
    :split {filename} 指定のファイルを新しいウインドウでオープンする

    またウインドウで新しいバッファを覗き見たい場合は:edit {filename}の用にタイプすることで可能です。

    続いてカーソルをウインドウの間で移動するには下記のようにします。

    フォーカスの移動

    コマンド 意味
    Ctrl-w w 次のウインドウに移動する
    Ctrl-w h 左のウインドウに移動する
    Ctrl-w j 下のウインドウに移動する
    Ctrl-w l 右のウインドウに移動する
    Ctrl-w k 上のウインドウに移動する

    ウインドウを閉じる

    ノーマルコマンド exコマンド 意味
    Ctrl-w c :clo[se] 現在アクティブなウインドウを閉じる
    Ctrl-w o :on[ly] 現在アクティブなウインドウ以外のウインドウを全て閉じる

    ウインドウのサイズを変更する

    コマンド 意味
    {N} Ctrl-w _ 高さをN行に指定する
    {N} Ctrl-w | 幅をN行に指定する
    Ctrl-w = すべての高さと幅を同じにする
    Ctrl-w _ アクティブなウインドウの幅を最大化する
    Ctrl-w | アクティブなウインドウの高さを最大化する

    またウインドウに関してもバッファや引数リスト同様に、全ウインドウに対してexコマンドを実行するコマンドが存在し:windoで実行することができますので、覚えておくと良いと思います。

    タブを理解する

    vimではタブもまた保持することができ、vimにおけるタブとはウインドウのコレクションになります。
    特に難しいことはないと思いますので、ここでは各種コマンドをご紹介するに留めたいと思います。

    タブを操作する

    コマンド 意味
    :tabedit {filename} タブを開く(同時に新しいウインドウ・バッファで開かれることになります)
    Ctrl-w T 新しいタブを開いてウインドウをそこに移動する
    {N}gt 番号指定でタブを移動する
    gt 次のタブへ移動する
    gT 前タブへ移動する
    :tabclose アクティブなタブをクローズする
    :tabonly アクティブなタブ以外のタブをすべてクローズする

    いかがでしたでしょうか。
    作業を効率化するために自分なりのタブやウインドウの設定を見つけていくことで快適な作業環境を構築できるんじゃないかなと思います。