PHPでテキストファイルに保存したログを一覧表示させたいと思います。

プログラムとデザイン(html)の部分を分ける為に、以下のようにしました。

// ファイルからデータを取得
$fp = fopen("log.txt","r");
$i=0;
while(!feof($fp)){
// ファイルから1行読み込み
$line = fgets($fp,1000);
// タブ区切りを配列にする
$array = explode("\t",$line);
// 表示用の配列に代入
$list[$i]["date"] = $array[0];
$list[$i]["ua"] = $array[1];
$list[$i]["host"] = $array[2];

$i++;
}

// 表示用
<?php for($i=0;$i<10;$i++){?>
<tr>
<td><?=$list[$i]["date"]?></td>
<td><?=$list[$i]["ua"]?></td>
<td><?=$list[$i]["host"]?></td>
</tr>
<?php }?>
※繰り返し箇所のみ掲載

しかし、上記の方法だと1万行の処理をするのに2~3秒の時間を要します。
一旦配列に代入しているので時間がかかるのは分かるのですが、
もしかしたら自分の書き方・考え方が間違っているのではないか?と思っています。
他に良い方法がありましたら、アドバイスいただければと思います。

※あくまでPHPとテキストファイルを使った上での実験ですので、DBを利用するという代替案は無しでお願いします。
※PHPは5.2.6を使っています。

回答の条件
  • 1人2回まで
  • 登録:2008/11/26 14:58:55
  • 終了:2008/11/27 10:54:32

回答(2件)

id:pahoo No.1

pahoo回答回数5960ベストアンサー獲得回数6332008/11/26 16:16:40

ポイント10pt

上記の方法だと1万行の処理をするのに2~3秒の時間を要します

ご質問のソースを、前半の(a)データ取り込み部分、と後半の(b)表示部分に分けます。

(a)だけで2~3秒かかっていますか? であれば、サーバのスペックなどをお知らせいただけますか。


一般的には、(b)の処理――というより、(b)の部分をブラウザで表示させるのに圧倒的に時間がかかります。

とくに table タグは、ブラウザの表示処理に時間がかかります。


本当にブラウザ表示する必要があるのかどうか疑問なのですが‥‥まずは、(a),(b)のどちらに時間がかかっているのかご確認ください。

id:k27w

aに時間がかかっています。配列($list)に代入する箇所をコメントアウトすると、瞬時に表示されます。


また、$listに代入している目的を書かないとご指摘を受けると思い書いた次第ですが、後半のB(HTMLに表示する箇所)を出力する・しない場合でも同じぐらい動作に時間がかかります。


テストしたサーバスペックはCore2Duo 1.83GHzの2GBですが、サーバスペックの問題ではないと思います。さくらやXREAなどのレンタルサーバで試した時も同じぐらいこの書き方だと遅いです。


あと、質問や記入漏れがある場合はコメント欄にいただけないでしょうか。

2008/11/26 16:59:54
id:tukihatu No.2

牛乳先生(tukihatu)回答回数180ベストアンサー獲得回数322008/11/26 18:50:48

ポイント60pt

特に考え方は間違っていないと思います。

一万行の処理をすれば、それ相応に時間もかかります。普通に2~3秒ぐらいはかかってしまうと思います。

基本的にメモリ依存なので。

それでもこれ以上早くしたいのなら、質問文に書いてあるとおり配列に代入するのをやめて直接その場で出力するぐらいしかないかと。

または、データの形式を変えるなど。(explodeしなくてもいいような仕組み、保存法を考えるとか)

最初の部分を全部消してから
// 表示用
<?php
$fp = fopen("log.txt","r");
$i=0;
while(!feof($fp)){
// ファイルから1行読み込み
$line = fgets($fp,1000);
// タブ区切りを配列にする
$array = explode("\t",$line);
?>
<tr>
<td><?=$array[0]?></td>
<td><?=$array[1]?></td>
<td><?=$array[2]?></td>
</tr>
<?php }?>

こんな感じ

確かにこれならばメモリをすぐ解放できるし、いくらか速くなるとは思いますが…

それでも2秒はかかると思いますよ。

ちなみにどちらの方法でも、あまり多い処理をすると、whileのタイムエラーで処理が止まる可能性があります。

UNIX系サーバならコマンドで動かすと早いと乗っています。

http://itpro.nikkeibp.co.jp/article/COLUMN/20080417/299352/

id:k27w

そうですね。やはり直接書くしかないですね‥。直接書いた場合は、私の環境では一瞬(1秒以内)でした。


使い勝手はDBよりテキストファイルの方が良いので実験していたのですが、処理面で制約があるので、大きいファイルを操作する場合は、DBに移行した方が良いかもしれません。


回答ありがとうございました。

2008/11/27 10:53:45
  • id:tezcello
    明確な対策案ではないので、こちらへ。

    データ読込み部分に時間が掛かっているのは、ひょっとして1行毎に読んでいる為?
    たぶんある程度はキャッシュ(というかバッファリング)されているとは思うのですけど...
    それなりの量があれば、複数回ディスクにアクセスする事になるのでは?

    データ量が多いと、file() で一気に読み込むってのも気が引けるなぁ...
    ってよく見たら結局は配列に全部入れているんですね。
    1行毎の処理と一気読みと速度差がどれほどあるのはチョッと不明です。
    (1万行のサンプルデータを用意できないので、テスト不可...)

    どうせ1行毎に読むなら、データを保持せずに書き出すまでやってしまうとか?
    (処理内容によっては出来ないですけど)

    PHPはデータを新しい配列に入れても、その内容が更新されるまでは参照で保持していると
    読んだ気がします。
    なので、出来るだけ内容を変更しないようなフローに変更すると処理が軽くなりませんか?
    (かなり推測で書いてます...)
  • id:k27w
    コメントありがとうございます。が、結局はどういう事なのでしょうか?
    「推測で~」と書かれているので、明確に私のソースが悪いのか
    他に良い書き方があるというわけでもないですよね?

    例えば
    「その書き方だとどうしても処理が遅くなるから○○した方が良い」
    というご意見や参考URLをいただければと思い質問しましたので、
    推測よりも答えがいただければ助かります。(もちろん答えは”回答”で)
  • id:tezcello
    書き込む前に終わってしまった...

    適当なのが無かったので、ローカルに転がっていたファイルで読込みテストをしてみました。
      全行同一書式で無いので読み飛ばし等の処理を追加。3万行ほど。

    fgets() で1行毎に読み、分割して、$list へ挿入 ... 0.542 秒
    file() で配列に一気に読み込み、foreach() で各要素を分割し $list へ挿入 ... 0.386 秒

    なので、どうせ一旦全部を取り込むのなら、一気にやった方が良いようですね。
    有意な差があるのかは微妙ですが。


    「推測」なのは、裏付け(実証データ)を持ち合わせていない為です。
    なので、fgets(), file() の比較テストをしてみました。
    また、変数をコピーしても値が変更されるまで実際のコピーは行われない件も
    読み込んだデータを全く何も処理せずに出力するとは思えなかったので、部分的にでも
    そういう使い方(=内容を書き換えない)をすれば多少は早くなるのだろうとのつもりでした。
    言葉足らずのコメントでごめんなさい。
  • id:k27w
    コメントありがとうございます。

    tukihatuさんの回答で、他に方法がないと思い、質問を終了させました。

    tezcelloさんがおっしゃる、fileを使う方法を試しました。
    私の環境では1万件を取得して20件表示するのに、1~2秒かかりました。
    質問時のソースよりは1秒短縮しました。

    質問に書いたようなソースの利用方法は、「ログ保存」になりますが、Apacheをはじめ、他のプログラムでも割と保存にテキストファイルを使っているので、何万行の処理でもWEB用途に利用出来るのではないか?っと思い、実験していました。

    ただ、おっしゃるように読み込んでデータになんらかの処理を加えるわけですから、どうしても処理が遅くなってしまいます。


    もしかしたらログ保存の方法か表示方法に問題があるかも知れませんね。もう少し考えてみます。コメントありがとうございました。
  • id:tukihatu
    >fileを使う方法
    一万件程度ならfileの方が確かにいいかも。
    でもfileだと一旦全部を取り込むので、あんまりにも行数が長いと処理が止まったりします…
    http://tt25.org/blog/20080510/each-lines
    昔に質問したやつ
    http://q.hatena.ne.jp/1208832776
    参考になれば。

この質問への反応(ブックマークコメント)

トラックバック

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません