* [flock] バッファ操作関連のまとめ [#rcf7f135] - ページ: [[BugTrack2]] - 投稿者: [[henoheno]] - 優先順位: 普通 - 状態: 提案 - カテゴリー: 本体バグ - 投稿日: 2006-03-20 (月) 00:02:44 - バージョン: #contents ** メッセージ [#j4ad2ce5] flock()周辺のバッファ操作(バッファのon/off、フラッシュ等)について、本来どのようにするべきであるかをシチュエーション別に明確にしましょう。 *** 参考文献: set_file_buffer() [#j8545212] - ソース: ext/standard/file.c PukiWiki 1.4.x は少なくともPHP4.1.2以降ターゲットにしているため、stream_set_write_buffer() ではなく set_file_buffer() を使用しています。 - PHPマニュアル: [[set_file_buffer()>PHPfunc:set_file_buffer]] - PHPマニュアル: [[stream_set_write_buffer()>PHPfunc:stream_set_write_buffer]] -- バッファのサイズを0にする事で、書き込みの最中にブロックしない事を保証する - [[STACK*:PHP中級>http://www.stackasterisk.jp/tech/php/php02_01.jsp]] -- 説明の仕方としては以下引用の通り バッファがあると、せっかく flock を使ってロックしても、書き込まれる前の データをディスクから読み出してしまうことがあるわけです - (PHP開発版のトピック) [PHP-users 14972]PHP 4.3.2RC2のstream_set_write_buffer(set_file_buffer)が失敗する -- http://ns1.php.gr.jp/pipermail/php-users/2003-April/015504.html -- http://ns1.php.gr.jp/pipermail/php-users/2003-May/015516.html PHP 4.3.2RC*ではローカルファイルへのユーザレベルIOバッファの機能は *無い*ので、set_file_bufferはするだけ無駄 -- Bug #23201 http://bugs.php.net/bug.php?id=23201 (Fixed at PHP 4.3.2) *** 参考文献: fflush() [#e5387b9f] - ソース: ext/standard/file.c - PHPマニュアル: [[fflush()>PHPfunc:fflush]] -- 一回の実行の間に「ファイルに書き込んでそれを再び読む」シチュエーションがある時、[[clearstatcache()>PHPfunc:clearstatcache]]を使用しないと、PHPのキャッシュによって実行時のファイル内容を読んでしまう旨コメント(by draco at lomag dot net)あり。 -- 再現方法 (by someone) $filename = 'testdata.txt'; $fp = fopen($filename, 'w+'); //set_file_buffer($fp, 0); fputs($fp, str_repeat('a', 10)); fflush($fp); echo filesize($filename)."\n"; fclose($fp); echo filesize($filename)."\n"; clearstatcache(); echo filesize($filename)."\n"; - fflush使わずset_file_bufferでも同じ結果にならないですか? -- &new{2006-03-21 (火) 01:22:41}; - 再現しない(同じ数値が表示される)けど、PHP本体のバージョンや設定が影響したりする? -- &new{2006-03-21 (火) 12:36:15}; #comment -------- ([[BugTrack2/151]]より移動) ** コメント: バッファ操作? [#bdd30207] - [[cvs:lib/file.php]](1.51)の変更ではファイル書き込みの短縮に主眼があるようですが、バッファリング無しにして数千行書き込めば遅くなるのも当然ではありませんか?&br;それから「put_lastmodified() の負荷軽減」なのですからrecent.datの処理だけでなく何故このタイミングでautolink.datの更新処理までしなければならないのかという点も検討した方がいいと思いますよ。 -- &new{2006-03-09 (木) 20:25:23}; -- こんにちは :) コードの機能分割については放っておいてもそうなるので大丈夫です。バッファ操作について確認させて下さい。set_file_buffer() を取れと言われていますか、それともさらに細かく追加しろと言われていますか?なおバッファを一旦切ることについては、理解している範囲では必須であるはずです。 -- [[henoheno]] &new{2006-03-11 (土) 21:44:13}; -- コードの機能分割云々は意味がよく分からないのでバッファ操作についてですが、flock処理しているところでは解放前に最低限OS側へデータを渡さねばならないですからバッファリング無しかバッファをフラッシュするかの2択しかないわけですよね。で、このコード周りはバッファ無しの方法を選択してループして一行ずつ出力しているという事なのでしょうが、ファイル出力なので塊にして出力した方がOS側がダーティーデータの管理を省けるのでスループットが上がるという単純な話です((遅延書き込みで無いOSでは他にも遅くなる理由がありますが))。メモリが勿体無いのであればバッファリング有りで解放前にフラッシュするほうを選択すれば良いだけです。ですがこの場合recent.datのファイルサイズは多くて1M程度と見積もっているのでこの場所でデータ連結用の一時変数を使っても構わないと踏んでます。こんなところで良いですか? -- &new{2006-03-12 (日) 00:44:36}; -- いえ。そのバッファ操作(切る場合、フラッシュする場合ですか)のコードをPHPで具体的にどうすべきとお考えなのかを提示していただけますか。 set_file_buffer() に関するコメントもお願いします。recent.datは件数ベースであれば仰る通り量は少ないと思います。メモリの確保と廃棄はflock()を取った後の方が良さそうですね(複数セット確保されるのは勿体無いので)。 -- [[henoheno]] &new{2006-03-12 (日) 01:16:41}; -- 具体的なコードは下のものですよ。set_file_bufferへのコメントとは取るか取らないかでしたらこのケースではサイズからいってどちらでもいいと思いますよ。最後のflockを取った後でというのはよくわかりません。 -- &new{2006-03-12 (日) 01:38:00}; - こんにちは。これで、最初のコメントで、どのように思われて「バッファリング無しに・・・」とコメントされていたのかがやっと判りました :) set_file_buffer() の意味を何か誤解されていませんか。(私が誤解しているなら指摘して下さい) -- [[henoheno]] &new{2006-03-16 (木) 00:22:23}; -- set_file_buffer() は万一のファイルの破壊を防ぐべく、出力バッファをクリアするために挿入された物のはずなので、破壊が起きるシチュエーションが絶対にないなら絶対にいらないし、可能性があるなら絶対に必要です。 -- [[henoheno]] &new{2006-03-16 (木) 00:23:39}; --- [[cvs:Attic/file.php]] (r1.40) -- 当時 [[ぱんだ]]さんがコミットしたコード --- PHPマニュアル: [[set_file_buffer()>PHPfunc:set_file_buffer]] --- [[STACK*:PHP中級>http://www.stackasterisk.jp/tech/php/php02_01.jsp]] - 私がここで明確にさせていただきたかったのは、「その提案は本当にバッファをコントロールしている事になるのか」と「バッファをさらにコントロールするために何か努力ができるのか、それともできないのか」です。例えば: -- [[henoheno]] &new{2006-03-16 (木) 00:26:31}; -- (1) flock() の直後で set_file_buffer() を使ってバッファを再設定すべきだ。そうすれば問題ない上にさらに早くなる。検証してみたが(以下略) -- (2) このシチュエーションでは set_file_buffer() は絶対に不要だ。その理由は(以下略) -- (3) このシチュエーションなら、fflush() をこう使って(以下略) - 取り扱うデータの量の大小でデータの破壊が左右されるはずがありませんから、それは set_file_buffer() を外す理由にならないと思います。 -- [[henoheno]] &new{2006-03-16 (木) 00:31:28}; - 確認しておいたほうがいいようですので、まず最初に下のコードの意図ですが、fputs()のコール回数を減らす事ではなくset_file_buffer()で停止したバッファリング動作を肩代わりする事に有ります。バッファ動作をコントロール下に置くと言い換える事もできるでしょうが、その場合の選択肢は12日に書いた2種類になります。続いて「万一のファイル破壊を防ぐ」の意味ですが、ここでのファイル破壊は排他処理が完全でなく別プロセスの処理によって引き起こされるものです。決してサーバー側の事故(停電・ディスククラッシュ等)によって起こされる破壊に対するものではありません。 -- &new{2006-03-16 (木) 20:36:55}; - それから、set_file_buffer()はバッファリングを切るために呼ぶのであってバッファサイズの変更などで呼ぶ必要はないでしょう。切らないのであればロック解放前にfflush()を呼ぶ必要があるだけです(何度も書いていますが)。文章だとどこまで理解してもらえているか判然としないので説明が難しいですねぇ…。 -- &new{2006-03-16 (木) 21:21:24}; #comment ---------------- ([[BugTrack2/151]]) ** コメント: fputs()のコール回数を減らす案 [#wca28f53] - ファイル書き込みを短縮するだけならこれを試してください(何も削らないが速度は改善するはず)。 -- &new{2006-03-10 (金) 22:04:58}; --- file.1.51.php Fri Mar 10 21:45:44 2006 +++ file.1.51.test.php Fri Mar 10 21:51:22 2006 @@ -303,7 +303,10 @@ arsort($recent_pages, SORT_NUMERIC); // Cut unused lines - $recent_pages = array_splice($recent_pages, 0, $maxshow); +// $recent_pages = array_splice($recent_pages, 0, $maxshow); + $data = ''; + foreach ($recent_pages as $page=>$time) + $data .= $time . "\t" . $page . "\n"; // Create recent.dat (for recent.inc.php) $file = 'recent.dat'; @@ -315,10 +318,12 @@ set_file_buffer($fp, 0); flock($fp, LOCK_EX); rewind($fp); - foreach ($recent_pages as $page=>$time) - fputs($fp, $time . "\t" . $page . "\n"); +// foreach ($recent_pages as $page=>$time) +// fputs($fp, $time . "\t" . $page . "\n"); + fputs($fp, $data); flock($fp, LOCK_UN); fclose($fp); + unset($data); // Create RecentChanges $fp = fopen(get_filename($whatsnew), 'w') or - コメントありがとうございます。fputs() のコール回数を減らす(CPUのオーバーへッド削減)という後半の部分が主題のようですね。ただ、そのために前半で配列を確保することになっています(メモリオーバーヘッド増加)。この一連の重たい処理(編集処理)の中で、さらにメモリを要求する事は避けたいところです。ただTipsとしてはありますよね :) -- [[henoheno]] &new{2006-03-11 (土) 21:52:03}; #comment