バッファリング処理を高速化-Perlプログラムで性能改善

このQ&Aのポイント
  • バッファリング処理を高速化したいです。Perlのプログラムで性能がでなくて困っています。ファイルの中身からレコードヘッダを検索しつつ可変長なレコードの件数をカウントするツールでバッファリングというかストリーミングっぽい処理をしているのですが、思いの外、性能が出ずこまっています。
  • 処理的なイメージはこんな感じになっています。LOOP_A: read(IN, $readbuf, 10240); $buf .= $readbuf; LOOP_B: ... レコード長の計算 ... バッファ内にレコード長以上なければ、LOOP_Aへ $buf = substr($buf,$reclen); # 処理済みレコードの削除 LOOP_B へ substr()の部分をC的にいうと先頭アドレスだけ移動するような書き方ができれば一番なのですが~
  • バッファリング処理を高速化するために、Perlプログラムの性能改善を図りたいです。現在、ファイルの中身からレコードヘッダを検索しつつ可変長なレコードの件数をカウントするツールを利用していますが、処理が非常に遅くなっています。特に、1レコード取得する部分とバッファシフトの部分が非効率なため、改善策を模索しています。先頭アドレスだけを移動させるような方法があれば、性能向上につながると考えています。正規表現を使用した1レコード取得の方法も検討中です。
回答を見る
  • ベストアンサー

バッファリング処理を高速化したいです。

バッファリング処理を高速化したいです。 perlのプログラムで性能がでなくて困っています。 ファイルの中身からレコードヘッダを検索しつつ可変長なレコードの件数をカウントするツールで バッファリングというかストリーミングっぽい処理をしているのですが 思いの外、性能が出ずこまっています。 (600MBのテキストで10分弱くらい、予定では1分程度で終わる見込みでした) とりあえず、非効率そうなのは、1レコード取得する部分と下記のようなバッファシフトの部分くらいなのですが バッファシフトについて何かもっとよい書き方はありますでしょうか? 処理的なイメージはこんな感じになっています。 LOOP_A: read(IN, $readbuf, 10240); $buf .= $readbuf; LOOP_B: ... レコード長の計算 ... バッファ内にレコード長以上なければ、LOOP_Aへ $buf = substr($buf,$reclen); # 処理済みレコードの削除 LOOP_B へ substr()の部分を C的にいうと先頭アドレスだけ移動するような書き方ができれば一番なのですが~ 1レコード取得は正規表現で探しているのですがこちらはもうすこし調べてからにしようと思います。

  • Perl
  • 回答数4
  • ありがとう数16

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

  • ベストアンサー
  • yuoke
  • ベストアンサー率53% (8/15)
回答No.1

確かに文字列の連結とか切り出しで効率は悪そうですが、ここを変えても、劇的には速くならないような気がします。 試しに、ファイルを読み込むだけの処理を書いてみて下さい。 次に、正規表現でレコード長を得ている処理を飛ばすため、正規表現の変わりに、レコード長を適当な数値に置き換えてみて下さい。 多分、ネックとなっているのは、正規表現の部分か、読み込みのどちらかだと思うので、たいして変わらないと思います。 その場合、正規表現の見直しとなります。 また、両者で速度が、大きく変わるのであれば、使用しているバージョンと、期待されるレコード長の平均サイズと、最大サイズを提示して下さい。

tsuduki123
質問者

お礼

substr()の問題では無いということでいろいろと実験してみた結果、 正規表現やsubstr()よりも複数ファイル読み込んだときにおこなうレコード件数の 集計部分が思った以上に時間かかっているということがわかりました。 集計部分をけずったら処理時間が1/3程度にまで減りました。 (10分強 → 3分強) 連想配列を使用してヘッダ種別ごとの件数をカウントしているので このあたりを工夫できないか考えてみます。 ちなみに、 perl version は 5.8.8 入力ファイル数 : 7000くらい(合計サイズは440MBくらい) 入力レコード件数: 82万くらい レコード長の平均は 512 バイトで 範囲は128 ~ 3072でした。 ヘッダの種類は 70種くらいです。 コメントありがとうございました。

その他の回答 (3)

  • yuoke
  • ベストアンサー率53% (8/15)
回答No.4

媒体やファイルシステムによっても違いますが、7000もファイルがあると、オープンとクローズだけでも、そこそこ時間が掛かるかもしれません。 念のため、確認することをお勧めします。

tsuduki123
質問者

お礼

集計処理を改善しても変わりませんでした。 すみません。実は原因は全く違うところでしたorz 入力ファイルがヘッダ種別から始まるか判断するのに cat -vt FILE | head -c -n 10 みたいな処理になっていて 判定後もファイルの内容を最後までよむ感じになっていました。 このチェックの方式を変更したところ、最初に相談したときのソースコードから 5-6行修正するだけで処理時間が10分強 → 4分弱まで減りました。 1ファイルにまとめたときは3分程度だったので あとの1分は集計部分なのかもしれませんが、7000ファイルで1分程度なら 一つ0.5秒程度だしまあいいかと思います。 4分程度なら本物のデータつかっても20分もあれば終わるでしょうから 待てない時間ではないので妥協しようと思います。 ありがとうございました。

回答No.3

なるほど、かなり難しいフォーマットであることがわかりました。 以下のようにすれば、もう少し効率化できるかもしれませんが、試していません。 # ヘッダごとにスキップする長さを書く my %templates = ( hd1 => 'x7', hd2 => 'x4', hd3 => 'x16' .... ); # ヘッダ名取得 $header = xx; # レコードの中身をスキップする $buffer = unpack $templates{$header} . 'a*' , $buffer;

tsuduki123
質問者

お礼

アプリの処理結果を検証するためのツールなので こんなめんどくさいことになっています;; 1ファイルだけにすると処理時間が圧倒に減ることに着目して いろいろと削って検証した結果、読み込みやカウントの部分には あんまり大きな問題はなくて、それ以前にロジックの問題でした。 問題点を修正したところ、4分弱までは短縮できたのでこれでしばらく運用してみようと思います。 ありがとうございました。

回答No.2

どんなフォーマットですか?

tsuduki123
質問者

お礼

フォーマットですが 改行コードなしで複数種類のヘッダがあります。 ヘッダにつづくデータ部分には改行コードが含まれていたりします。 レコード長の情報は別に存在しますが参照できません。 その代わり、ヘッダと同じ文字列はデータ部には現れない前提です。 現れた結果、件数が想定外になってもその部分に関しては問題ないことになってします。 このツールの目的は ファイル単位ヘッダ種別ごとにレコード件数を出力するのと 最後にヘッダ種別ごとのレコード件数合計を出力することです。 ファイル内の書式例です。 hd1zzzzzzzhd2xxxxhd3gggggggggggggggghd1zzzzzzz hd? の部分がヘッダになります。 ヘッダは全部で70種類くらいありますがすべてのヘッダがファイル内に含まれているとは限りません。 ファイル数は7000ファイルくらいが上限ですが、ファイルサイズの合計は4GB程度になります。 別の回答に記述しましたが、性能が出ない原因はレコード件数の集計処理にあるようでした。 同じデータ量でも1ファイルだけにすると処理時間が1/3程度になることから レコード件数の合計を管理している連想配列とファイル単位のレコード件数を管理している連想配列の加算処理に問題があるのかもしれません。 substr()を使わなくてもよいように読み込んだバッファを正規表現 1回で レコード単位の配列に入力する処理へ変更してみましたがかえって遅くなってしまいました。 # shiftとかpopしているせいかもしれませんけど。。。 substr()を使って地道に処理する方が早いようです。 #ん~substr()って優秀なんですね。

関連するQ&A

  • LOOP処理で処理の先頭に移動するには?

    数年ぶりにVBのプログラミングをする事になり超初心者状態のため稚拙な質問で申し訳ございません 以下のような感じでLOOP処理を作成しています。 DO UNTIL AAA.EOF チェックA チェックB チェックC  AAA.MOVENEXT LOOP 上記のような形で取得した内容をLOOPでまわし、 一レコードづつチェックをかけていくのですが、 たとえば、チェックBでエラーとなった段階で AAA.movenextして チェックcはスキップしLOOPの先頭に移動し 次レコードのチェックをチェックAから開始したいのですが、 このような場合どう記述するのでしょうか? EXIT DOでループ自体から抜けるやり方はありましたが、このように処理の 途中で以降の処理をスキップしてループの先頭に移動する 方法が見つかりませんでした。 どなたかご教授ください!!

  • 構造体をディスクからロードして複数バッファリングさせる

    構造体をディスクからロードして複数バッファリングさせておくようなプログラムを組みたいと思い、 C言語で下のようなコードを記述しました。 ※質問にあたり最小限の記述にしています。 #define SIZE (sizeof(struct hoge)) #define NUM 16 char buf[SIZE*NUM]; fd=open(file_name,O_RDONLY); read(fd,buf,SIZE*NUM); 上のような記述(read)をして構造体(hoge)を16個分確保できてしまいました。 ここで疑問に当たりました。 (1) read(fd,buf,SIZE); (2) read(fd,buf,SIZE*NUM); 個人的には(1)と書いても(2)のように書いて、fdが指すポインタから、SIZE分読み込んだ時点で、ファイルの末尾に達するので読み込みが終わると思っていました。 SIZE*NUM分読めたことに納得がいきません。 末尾にきたらファイルの先頭に戻っているのでしょうか? readのmanを読んでも間違いないと思えるのですが。。 お分かりの方いらっしゃいましたらぜひぜひ教えてくださいませ。

  • windowsのリアルタイム処理について

    下記の不具合があり困っております。 画像入力ボードを開発し、数msごとに数kbの画像をwindowsのドライバへ取得割り込みをかけております。ドライバはそのデータを取り込みリングバッファに貯めていきます。アプリケーションはそのリングバッファのデータをリングバッファがあふれないように定期的に取り込み、画像認識しながら画面に表示します。このときパソコンに他のアプリが走ったときに画像が乱れます。このときCPU負荷率は80%程度になっておりリングバッファから抜いたデータが異常になっております。ドライバがPCIの割り込みに応答して画像データを取得しリングバッファを更新するのがまずいように思います。 アプリケーションがドライバから受け取ったデータを検証すると異常なデータが入っていることからドライバの動作がおかしいことまではわかりました。 そもそもリアルタイム処理に無理があるのでしょうか? 何か良い対策はないでしょうか?もちろんCPU負荷率が小さいときは正常に稼働しています。

  • _IOFBF の定義が、異なる

    int setvbuf(FILE *stream, char *buf, int mode, size_t size) stdio.h streamのバッファリングを制御する.buf がNULLでなければ,それがバッファとして使われる.NULLならば割り当てられる.バッファの大きさはsizeによって決まる.エラーが起きれば,0 でない値が返る. mode _IOFBF フルバッファリング. _IOLBF 行バッファリング.改行文字出力時にフラッシュされる. _IONBF バッファリングなし.buf ,sizeは無視される. 上記の関数の引数で _IOFBF _IOLBF _IONBF の3つの乗数の定義がコンパイラー(A/B/C)によって異なっていました       A      B       C _IOFBF 0x0000 / 0x0001 / 0  _IOLBF 0x0040 / 0x0002 / 1 _IONBF 0x0004 / 0x0004 / 2 本当に正しい定義はどれか? それは決められるものなのか? AコンパイラーのDLLから、BコンパイラーのDLLを呼び出したところ 不具合に気が付きました この様な状況の時、皆さんならどのようにこの問題を回避(対応)しますか?

  • ループ処理の繰り返しについて

    お世話になっております。 単純なことで悩んでおります、どなたかわかるかたお願いいたします。 <% DO UNTIL SQLrs.EOF IF a=0 THEN %> 処理1 <% END IF SQLrs.MOVENEXT LOOP %> <% SQLrs.MOVEFIRST DO UNTIL SQLrs.EOF IF b=0 THEN %> 処理2 <% END IF SQLrs.MOVENEXT LOOP %> 上記のような処理順なのですが、問題は MoveFirstで先頭のレコードにもどらないのか、2回目のループ処理がうまく抽出できません。 原因はわかりますでしょうか?

  • レコード一件ずつ処理判定をしたいのです。

    データベースの値を取得してきて 下記内容の処理を実施したいと考えています。 Do While rs.EOF = False    処理する内容:     レコード1件目とレコード2件目の値をチェックする。     レコード2件目とレコード3件目の値をチェックする。 rs.MoveNext Loop レコード一件ずつ処理の判定をしたいのです。 要は レコード内で重複する値があれば排除する分岐をしたいんですよね。

  • URLクラスでのリード時のバッファサイズについて

     URLクラスでインターネットアクセスするときに、以下のようにプログラムする場合、望ましいBUFSIZEはあるのでしょうか。  inputstream.read(byte_buf,offset,BUFSIZE) A  プログラマーは任意のバッファサイズを指定して、なんら問題はない。  仮に1であっても、実際はJavaのほうでネットアクセスするに最適なサイズでアクセスし、そこから1バイトずつくれるので問題はない。同様にして、メモリの許す限りに大きいバッファサイズも問題はない。 B  Javaはコーディングどおりに、サーバーに1バイトを要求するので、ファイルダウンロードする方法としてとんでもないことだ。同様にして、一度に何十メガバイトもの指定もとんでもないことだ。    AとBで、どちらが正しいでしょうか。Bならば、のぞましいバッファサイズと、その根拠を教えてください。通常、ダウンロードするときのネットマナーみたいなものはあるのでしょうか。

    • ベストアンサー
    • Java
  • 排他処理について

    VBでトランザクションをかけてあるテーブルを処理しています。 具体的にはテーブルAのレコードを一時的に別テーブル(テーブルB)に コピーしてそのレコードすべてを元のテーブルから削除して、 テーブルB内で処理をさせてから、そのデータをテーブルAに戻して コミットしています。 この処理中に、別PGもしくはSQLPlusでテーブルAからテーブルBに コピー対象の1レコードを削除してコミットしました。 これってエラーになる?ならない?それともトランザクション中だから テーブルAがコミットされて開放されるまで待ちの状態になる?? 実際どうなったかというとエラーにならなくて、その処理で最後に テーブルAにテーブルBをコピーしたときにエラーになって データがきえるという現象がおきてしまいました。 上記のような処理のときにOracle条ではどのような処理になるのでしょうか? よろしくお願いします。

  • Access テーブルの変更点を一覧に表示したい。

    お世話になります。 AとBと全く同じ構造のテーブルがあります。 Bにデータを流し入れ、Aとの差分を取得し、アプリケーションの一覧にデータの変更があった箇所を分かるように表示させたいです。AとBとの差分のレコードはAとBを結合したSQLを元に取得できるのですが、レコードのどのフィールドに変更があったのかまでは分かりません。 この場合、対象レコードのフィールド分、ループ処理を行い、データを比較していくしかないのでしょうか?フィールド数が多いため、1レコード、1フィールドづつ比較するのは処理に時間が掛かりそうです・・・。 こういった場合、どうすれば良いのか迷っております。 何卒、アドバイスを宜しくお願いします。

  • 重複レコードを高速で取得するSQL

    Access(mdb)から約2万件レコードのあるテーブルがあるとします。 列数は20ほど。 その中から、3つの列において重複しているレコードを取得したいのですが、 高速に取得する方法はありますでしょうか? (VB.NETで、重複レコードをユーザーに示す処理を作成したいのです) 以下のSQLを試したところ、1分以上時間がかかってしまいました。 ----------------------------- SELECT * FROM テーブルA table1 WHERE EXISTS ( SELECT * FROM テーブルA table2 WHERE table1.列A = table2.列A   table1.列B = table2.列B   table1.列C = table2.列C GROUP BY table2.列A HAVING COUNT(table2.列A) > 1 ) ----------------------------- アドバイスをお願いします。