こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

Perl ページ表示遅い ループの改善

ソースが汚いかもしれませんが、ご教示お願い致します。

実行したいことは、できてはいるのですが、
「表示が遅い」です。
ループのやり探し、
サーバー上でのHTML生成が
原因だと思うのですが、
どう改善すればいいのかわかりません・・・

・下記のソースの簡単な説明
あるテキストファイルにCSV方式でデータを記録してあります。
(最終で200万バイトくらい)
(現在 12万バイト)
そのデータで1列目に順位の数字があり
(~20位前後)
それを、いろんな条件で絞込し一気に表示してます。

そこから、サーバー上でテーブルとして表示させているのですが
とにかく遅いです。
・・・サーバー上で計算するからでしょうが・・・

もう少し速くする方法はないでしょうか?
スクリプトでHTMLを生成させることはできるのですが、
サーバーにデータのみを保存させ
そこから生成させたいのです・・・

※だいたい10秒前後(Wi-Fi有りで)
Wi-Fiの環境がなく、さらに回線が3Gまでに
なっている人だと絶望的な状況だと・・・

下記のソースで、
(1)おかしな点
(2)改善すれば速くなる点
(3)別の案

を教えてほしいです・・・

------ソース------
my @DataNo = ("13","14","2","1","8");
my $DataName;
for(my $dn=0;$dn<=$#DataNo;$dn++){
 my $DataNo = $DataNo[$dn];
 my ($No) = split(/,/, $DataNo);
 
 if($No eq "13"){ $DataName = "A" }
 if($No eq "14"){ $DataName = "B" }
 if($No eq "2"){ $DataName = "C" }
 if($No eq "1"){ $DataName = D" }
 if($No eq "8"){ $DataName = "E" }

print <<END;
 <h2 class="ResMidashi"> $DataNameデータ</h2>
  <table class="ResultData">
   <tr>
    <td>[$DataName]</td>
    <th>Test1</th>
    <th>Test2</th>
    <th>Test3</th>
   </tr>
END

my %hash = ();
my @arrey;
open(F, "<","../Test/Test.txt") or die("error :$!");
eval{ flock(F, 1) };
while(<F>){
 chomp;
 my @temp1 = split /,/;
 push @arrey , $temp1[$No];
}
close F;

foreach (@arrey) {
 $hash{$_}++;
}

my @Data;
if($No eq "1" || $No eq "2" || $No eq "9"){
 @Data = sort keys %hash;
}
else{
 @Data = reverse sort keys %hash;
}

my $matches = 0;
my $Count1 = 0;
my $Count2 = 0;
my $Count3 = 0;

for(my $Co=0;$Co<=$#Data;$Co++){
 my $DATA = $Data[$Co];
 my ($Main) = split(/,/, $DATA);

 open(F, "<","../Test/Test.txt") or die("error :$!");
 eval{ flock(F, 1) };
 while(<F>){
  chomp;
  my @temp2 = split /,/;
  if( $temp2[5] eq "$JoukenA" and $temp2[6] eq "$JoukenB" and $temp2[7] eq "$JoukenC"){
   if( $temp2[$No] =~ /^$Main$/ ){
    $matches++;
    $Count1++ if $temp2[0] == "1";
    $Count2++ if $temp2[0] == "2";
    $Count3++ if $temp2[0] == "3";
   }
  }
 }
 my $T;my $P;
 if($matches){
  $T = ($Count1 / $matches) * 100;
  $T = sprintf('%.2f', $T);
  $P = (($Count1 + $Count2 + $Count3) / $matches) * 100;
  $P = sprintf('%.2f', $P);
  my $Frame1;my $Frame2;

  if($Puk ne "0.00"){
print <<END;
   <tr>
    <td>$Main</td>
    <th>$Count1-$Count2-$Count3-$matches</th>
    <th>$T</th>
    <th>$P</th>
   </tr>
END
  }
 }
 $matches = 0;
 $Count1 = 0;
 $Count2 = 0;
 $Count3 = 0;
 if($Co eq $#Data){last;}
}
print <<END;
  </table>
END
}

投稿日時 - 2018-02-07 09:10:25

QNo.9426464

困ってます

質問者が選んだベストアンサー

> どう変更すればいいでしょうか?

申し訳ないですが、コードそのものを作る時間がありません。
また、ご自身でメンテナンスできないコードを受け取っても、今後、困ると思います。

ですが、フローチャートとしては
ファイル読み込みループ
 No列ごとのキーワードを取り出し(一行に5種類)
 5つのNoごとに、上記キーワードをハッシュキーにした配列のハッシュを作る
 検索条件が一致したら、上記のハッシュ内の配列を使って検索条件別にカウント
ファイル読み込みループ終了
Noの5つでループ
 Noごとのハッシュをハッシュキーでソート
 Noごとのハッシュの要素数ループ
   ハッシュの配列の値を使って出現回数等を計算して、HTML生成
 ループ終了
ループ終了
って感じでつくればよいと思います。


もしくは、ファイルの中身を配列にいれて
いまのアルゴリズムのまま、ファイルのループを配列のループに変更するだけにするか。

投稿日時 - 2018-02-07 17:09:34

お礼

いろいろ試行錯誤を繰り返してみたのですが
いまの私の理解力では、できませんでした。。。

なので、妥協策として
5つのループを別々にしました・・・

多少速くなったと思います
また表示したいデータが増えてきてソース修正が
大変に感じたときに、また考えます・・・;;

投稿日時 - 2018-02-14 15:20:26

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(2)

ANo.1

一回の処理で、$Noを変えながら5回ループがあって
そのループの中で、Test.txtの特定の列からキーを取り出して、
ハッシュを使って重複を取り除いて、キーでソートして
そのキーの個数回だけ、再度 Text.txtから読み込みしてるんですね。

結果、5×(1+キー種類)の回数だけ、ファイルを読み直ししているので
これは遅いでしょうね。

1回の読み込みで キーを取り出ししながら
条件一致のカウントは、キー別のハッシュ(ハッシュの中身は4要素の配列)で数えておいて
最後にキーでソートして、それぞれのカウント結果や計算結果をHTML化
とするように変更できますね。


ハッシュの中身を配列にするというのが、難しければ、
12万バイト っていうことは 0.117MByte 、200万バイトでも2MByte程度なので 
メモリーに入るサイズなので、ファイルの中身を全部をメモリー(配列)に入れて、
その配列のループで、今と同じアルゴリズムで検出するっていう方法もありますが。

あと、気になったのは
ループ後半の条件検索のとこのファイル読み込みですが、
flockして読み込んだ後に
 close F;
してないのでファイルロックがかかったままで、次のループで再度flockさせています。
read属性同士での再ロックなので、今のところ大丈夫かもしれませんが
デッドロックの温床になりやすいので、ここは修正したほうがよいでしょう。

投稿日時 - 2018-02-07 15:41:56

お礼

御回答ありがとうございます。

>>
結果、5×(1+キー種類)の回数だけ、ファイルを読み直ししているので
これは遅いでしょうね。

<<やはりここですよね・・・

>>1回の読み込みで キーを取り出ししながら
条件一致のカウントは、キー別のハッシュ(ハッシュの中身は4要素の配列)で数えておいて
最後にキーでソートして、それぞれのカウント結果や計算結果をHTML化
とするように変更できますね。

<<これを実行したいのですが、私ハッシュの使い方がいまいちわかっておりません・・・

push @arrey , $temp1[$No];
foreach (@arrey) {$hash{$_}++;}

これらを変更するのでしょうが・・・
どう変更すればいいでしょうか?

>>close F;
ご指摘ありがとうございます。
これはループ外におき修正しました

投稿日時 - 2018-02-07 16:14:21

あなたにオススメの質問