質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

7912閲覧

php://temp/maxmemory に書き込むより、ローカルファイルに書き込んだ方が早い理由について

al_aya_yuka

総合スコア98

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

3クリップ

投稿2014/11/28 02:27

素朴な疑問です。
数百MBのCSVファイルをPHPで処理しています。
ファイルのエンコードがcp932でfgetcsvで誤作動するのでutf-8に変換しています。
ファイルそのものを変更する必要はないので、テンポラリに格納しようとしています。
そのテンポラリファイルが実ファイルより、メモリに書き込んだ方がはるかに遅いです。
想定ではメモリの方が比較にならないほど早いと思っていたのですが…
これはなぜなんでしょうか。
それとも扱い方が悪いのでしょうか。

csvのファイルサイズ: 約120MB~500MBほど
memory_limit: システムの空きメモリを見て動的に設定(テストでは3GBほど)
TMP_FILE_MAX_MEMORY: memory_limit の3/2

ご教授のほど、よろしくお願いします。

lang

1// 遅い!! 2//$fp = fopen("php://temp/maxmemory:".TMP_FILE_MAX_MEMORY, 'r+b'); 3// 普通 4$fp = fopen("C:/test.csv", 'w+b'); 5 6$tfp = fopen($csv_file, "rb"); 7while(($line=fgets($tfp))!==false){ 8 // この行をコメントアウトすると一瞬で終了 9 // つまり、読み込みのオーバーヘッドは関係ないと思います 10 fwrite($fp, mb_convert_encoding($line, 'UTF-8', 'sjis-win')); 11} 12fclose($tfp); 13rewind($fp);

Windows7
PHP 5.6.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

確信のある回答ではないことを、初めにお断りしておきます。

通常のファイルにfwriteする場合、(OS側で一定のバッファがあるとはいえ)書き出してしまえばメモリにデータは残りません。一方で、PHP側でテンポラリメモリに入れると、まさにその分だけPHP管理のメモリに入るわけで、しかもデータ量は行を追うごとに延びてきます。

つい最近どこかで見たのですが、PHPの実行時間のうち3割程度が動的メモリ操作で消費されているとのことで、この例のように「徐々に領域を延ばして100MB単位のメモリを消費する」ような使い方は、ただでさえ処理の重いメモリマネージャに必要以上の負担をかけてしまっているのだと思います(なお、PHP 7で改善予定ではありますが)。

投稿2014/11/28 03:05

maisumakun

総合スコア145064

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

al_aya_yuka

2014/11/28 03:57

回答有難うございます(*^_^*) すごく納得の行く内容でした。 でもそうなってくるとphp://memory はどこで威力を発揮できるのでしょうね。。。
guest

0

ベストアンサー

直接の回答では無いですが

ファイルのエンコードがcp932でfgetcsvで誤作動するのでutf-8に変換しています。

このアプローチ自体が誤っていると思います。以下のようにロケール設定をすることで正しい読み出しが可能です。

【php】fgetcsv()はロケールの設定に依存する

lang

1setlocale(LC_ALL, 'ja_JP.sjis'); 2$fp = fopen('C:\test.csv', 'rb'); 3while ($row = fgetcsv($fp)) { 4 mb_convert_variables('UTF-8', 'SJIS-win', $row); 5 // $rowで何かする 6}

…しかし、この方法だと結局何回も mb_convert_variables 関数を呼び出すことになってオーバーヘッドが大きいので、以下のようにストリームフィルタを使う方が理想的かもしれません。こっちだとOSに依存もしないので、移植性の面からも優れていると言えると思います。

ストリームフィルタで文字コード変換してみる

lang

1$fp = fopen('C:\test.csv', 'rb'); 2stream_filter_prepend($fp, 'convert.iconv.cp932/utf-8', STREAM_FILTER_READ); 3while ($row = fgetcsv($fp)) { 4 // $rowで何かする 5}

もしテンポラリファイルを使うにしても、tmpfile 関数を使うのがおすすめです。

投稿2014/11/28 03:53

mpyw

総合スコア5223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

al_aya_yuka

2014/11/28 04:04

間違えて、BAを押してしまいました。。。 ロケール依存の話は多少知識がありまして、仰るとおりなのですが、それではいわゆる5C問題が解決できなかったのです。 CSVのデータは「表」「貼」「ソ」を含んでおり、その行自体がおかしなことになってしまうのです。 しかしながらmb_convert_variablesやストリームフィルタは知らなかったことなので、実践してみようと思います。 ありがとうございました。
guest

0

気になったので試してみた感じ php://memory などが異様に遅くなるのは Windows 特有のようです。

下記はいずれも PHP 5.6.3 で、CentOS 6 は remi-56 のバイナリ、Windows 7 は公式の x64 nts のバイナリで、-n オプション付きで実行しています。

(CentOS 6 はディスクイメージが iSCSI の先にある HDD 上のファイルな仮想環境なので I/O が普通より遅いかもです )

変数に追記

lang

1<?php 2$m = microtime(true); 3 4$a = ""; 5for ($i=0; $i<100; $i++) { 6 $a .= str_repeat("x", 1024*1024); 7} 8unset($a); 9 10var_dump(microtime(true) - $m); 11var_dump(memory_get_peak_usage()); 12 13// CentOS 6 14// float(0.051472187042236) 15// int(106134088) 16 17// Windows 7 18// float(3.6982111930847) 19// int(106144824)

php://memory に追記

lang

1<?php 2$m = microtime(true); 3 4$fp = fopen("php://memory", "r+b"); 5for ($i=0; $i<100; $i++) { 6 fwrite($fp, str_repeat("x", 1024*1024)); 7} 8fclose($fp); 9unset($fp); 10 11var_dump(microtime(true) - $m); 12var_dump(memory_get_peak_usage()); 13 14// CentOS 6 15// float(0.049097061157227) 16// int(106135176) 17 18// Windows 7 19// float(14.518830060959) 20// int(106146232)

HDD上のファイルに追記

lang

1<?php 2$m = microtime(true); 3 4$fp = tmpfile(); 5for ($i=0; $i<100; $i++) { 6 fwrite($fp, str_repeat("x", 1024*1024)); 7} 8fclose($fp); 9unset($fp); 10 11var_dump(microtime(true) - $m); 12var_dump(memory_get_peak_usage()); 13 14// CentOS 6 15// float(1.1221830844879) 16// int(1277448) 17 18// Windows 7 19// float(0.20701193809509) 20// int(1288560)

tmpfs上のファイルに追記(CentOS のみ)

lang

1<?php 2$m = microtime(true); 3 4$fn = tempnam("/dev/shm/", "xxx"); 5$fp = fopen($fn, "r+b"); 6for ($i=0; $i<100; $i++) { 7 fwrite($fp, str_repeat("x", 1024*1024)); 8} 9fclose($fp); 10unset($fp); 11unlink($fn); 12 13var_dump(microtime(true) - $m); 14var_dump(memory_get_peak_usage()); 15 16// CentOS 6 17// float(0.046942949295044) 18// int(1278472)

投稿2014/12/05 07:02

ngyuki

総合スコア4514

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問