記事一覧

PHPでtailコマンドを作ってみた




目次



ファイル読み込みについて

ハロー、みなさん。エジソンです。

最近、ファイル読み込みに関して気になることがあって、ずっと調べていたことがありました。それは、ファイル読み込みにおいて、内容を全て読み込む場合と、一部を読み込む場合とで、どれだけパフォーマンスが違うのかということです。

普通に考えてみれば、全て読み込む方が遅いのは明らかですが、そうは思っていても、実際にどれぐらいパフォーマンスが違うのかということを把握していませんでしたので、実際に計測してみることにしました。

ファイルの全読み込みと一部読み込み時のパフォーマンス比較

以下のファイルを作成(100KByteのファイル、100MByteのファイルをそれぞれ用意)して、全読み込みと、一部読み込みのパフォーマンスを測定してみました。

本プログラムを実行する上での、ファイル構成は以下のようになっています。

  • fileread.php
  • fileread_test100kb.txt
  • fileread_test100mb.txt

実行環境は以下の通りです。



プログラムの本体は以下の通りです。ファイルの読み込みには、file_get_content を使用します。

fileread.php
<?php

/*
 * 100KB ローカルファイルをすべて読み込む
 */
$time_start = microtime(true);
$fileContents = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'fileread_test100kb.txt');
echo "100kb  all process time : " . sprintf('%.6f', microtime(true) - $time_start) . PHP_EOL;

/*
 * 100KB ローカルファイル末尾の100文字読み込む
 */
$time_start = microtime(true);
$fileSize = filesize(__DIR__ . DIRECTORY_SEPARATOR . 'fileread_test100kb.txt');
$fileContents = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'fileread_test100kb.txt', false, null, $fileSize - 100, 100);
echo "100kb part process time : " . sprintf('%.6f', microtime(true) - $time_start) . PHP_EOL;

/*
 * 100MB ローカルファイルをすべて読み込む
 */
$time_start = microtime(true);
$fileContents = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'fileread_test100mb.txt');
echo "100mb  all process time : " . sprintf('%.6f', microtime(true) - $time_start) . PHP_EOL;

/*
 * 100MB ローカルファイル末尾の100文字読み込む
 */
$time_start = microtime(true);
$fileSize = filesize(__DIR__ . DIRECTORY_SEPARATOR . 'fileread_test100mb.txt');
$fileContents = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'fileread_test100mb.txt', false, null, $fileSize - 100, 100);
echo "100mb part process time : " . sprintf('%.6f', microtime(true) - $time_start) . PHP_EOL;


測定結果
100kb  all process time : 0.000473
100kb part process time : 0.000075
100mb  all process time : 0.193142
100mb part process time : 0.014332

結果を見ると、やはり”一部読み込み方式”が高速です。

しかし、一方でファイルサイズの違いに注目すると、ファイルサイズの大小でもパフォーマンスには差が出ています。100KByteよりも、100MByteのファイルの方が、低速であるという結果になりました。

当初、一部読み込み方式では、ファイルサイズの大小に関わらず一定の速度が出るのではないかと予測していましたが、そんなことはなく、ファイルサイズがボトルネックになり、速度が落ちてしまうことがわかります。

とはいえ、巨大なファイルを局所的に読み込む場合には、”一部読み込み方式”を採用した方が良いとの結論が出ました。

今回は、PHPのfile_get_contentsを利用した計測ですので、別の関数や別の言語を採用することで、また違った結果が得られるかもしれません。

tailコマンドを自作してみよう

一部読み込み方式のナレッジを得たところで、応用編として、tailコマンド(tail -f)を自作してみることにします。

Wikipedia - Tailコマンド

プログラムは以下の通りです。

tail.phpを実行する際の第一引数にファイルパスを与えると、当該ファイルの内容が標準出力されます。ファイルの内容を常に監視していますので、ファイル内容が変更されるたびに、変更部分が標準出力されます。

<?php

/*
 * 使用方法:
 * tail.php {ファイルパス}
 */

// テスト用コード
//$argv = array();
//$argv[] = __DIR__ . DIRECTORY_SEPARATOR . 'tail.txt';
//$argv[] = __DIR__ . DIRECTORY_SEPARATOR . 'tail.txt';

if (count($argv) <= 1) {
	echo 'ファイルパスが指定されていません。';
	return;
}

$file = $argv[1];

$lastFileSize = 0;
$readOffset = 0;

while (true) {

	// ファイルサイズの取得
	clearstatcache(true, $file); // filesize関数のキャッシュをクリアする
	$fileSize = filesize($file);

	if ($lastFileSize !== $fileSize) {

		// ファイルサイズに変化がある場合に、前回からの変更部分を出力する
		$fileContents = file_get_contents($file, false, null, $readOffset, $fileSize);
		echo $fileContents;

		// 最後に更新されたファイルサイズを保持しておく
		$lastFileSize = $fileSize;

		// オフセット位置を更新する
		$readOffset = $fileSize;
	}

	// N msec スリープする
	usleep(500 * 1000);
}


関連記事

このエントリーをはてなブックマークに追加

コメント

コメントの投稿

非公開コメント

プロフィール

EZOLABブログへようこそ。
EZOLABは、札幌のソフトウェア会社です。

http://ezolab.co.jp