11

非常に大きなデータ ファイルがいくつかあり、ビジネス上の理由から、大規模な文字列操作 (文字と文字列の置換) を行う必要があります。これは避けられません。交換回数は数十万回にも及びます。

思ったよりも時間がかかっています。PHP は一般的に非常に高速ですが、私はこれらの文字列操作を非常に多く行っているため、速度が低下し、スクリプトの実行に数分かかります。スクリプトは頻繁に実行されるため、これは苦痛です。

いくつかのテストを行ったところ、str_replaceが最速であり、strstrpreg_replaceの順であることがわかりました。また、個々の str_replace ステートメントを試したり、パターンと置換の配列を構築したりしました。

文字列操作操作を分離して別の言語で書くというアイデアをいじっていますが、そのオプションに時間を費やして、改善が無視できることを確認したくありません。さらに、私は Perl、PHP、COBOL しか知らないので、他の言語についてはまずそれを学ばなければなりません。

他の人が同様の問題にどのように取り組んだのだろうか?

検索しましたが、これが既存の質問と重複しているとは思いません。

4

9 に答える 9

1

置換を複数回実行できるようにする場合は、各ファイルを処理するスクリプトを作成し、コンテンツが重複する置換ファイルを一時的に作成できます。これにより、あるファイルから別のファイルにデータを抽出し、コピーを処理してから変更をマージできます。または、ストリーム バッファーを使用する場合は、各行を記憶できるため、コピー/マージの手順をスキップできます。

ただし、問題は、ファイルを完了せずに処理し、混合してレンダリングすることです。したがって、一時ファイルが適しています。

これにより、変更が必要な回数だけスクリプトを実行できます。必要なのは、処理されたファイルを記憶する一時ファイルだけです。

于 2012-12-04T11:12:04.953 に答える
1

制限要因は、PHP による文字列の再構築に関するものです。検討:

$out=str_replace('bad', 'good', 'this is a bad example');

文字列内の 'bad' を見つけるのは比較的低コストの操作ですが、置換の余地を作るために、PHP は文字 e、l、p、m、a、x、e のそれぞれを上に移動する必要があります。新しい値を書き込む前にスペース。

針と干し草の山に配列を渡すと、パフォーマンスは向上しますが、それほどではありません。

私の知る限り、PHP には低レベルのメモリ アクセス関数がないため、最適なソリューションは別の言語で記述し、データを「ページ」に分割して、変更に対応するために拡張する必要があります。chunk_split を使用して、文字列をより小さな単位に分割することもできます (したがって、置換ごとにメモリのジャグリングが少なくて済みます)。

別のアプローチは、ファイルにダンプして sed を使用することです (これは、一度に 1 つの検索/置換を実行します)。

sed -i 's/good/bad/g;s/worse/better/g' file_containing_data
于 2012-12-04T11:19:20.350 に答える
1

まあ、PHP では一部の文字列操作が配列操作よりも高速であり、その速度にまだ満足していないことを考えると、おそらく「低レベル」言語で、言及したように外部プログラムを作成できます。C/C++ をお勧めします。

于 2012-12-04T11:02:16.803 に答える
1

これを処理するには、IMO の 2 つの方法があります。

  • [簡単]バックグラウンドプロセスでいくつかの一般的な置換を事前計算し、それらをDB /ファイルに保存します(このトリックは、すべての正弦と余弦が一度事前計算されてからRAMに保存されるゲーム開発から来ています)。ただし、ここでは次元の呪いに簡単に遭遇する可能性があります。
  • [それほど簡単ではない] C++ またはその他の高速でコンパイル可能なプログラミング言語で代替ツールを実装し、後でそれを使用します。Sphinxは、C++ で実装された大量のテキスト データ セットに対する高速操作ツールの好例です。
于 2012-12-04T11:11:27.230 に答える
0

この操作が 1 回だけで、静的コンテンツに置き換える必要がある場合は、Dreamwaver などのエディターを使用できるため、PHP は必要ありません。それははるかに高速になります。

それでも、これを PHP で動的に行う必要がある場合 (データベース レコードなどが必要な場合) は、exec経由でシェル コマンドを使用できます-検索置換の Google 検索

于 2012-12-04T11:12:03.140 に答える
0

PHP で壁にぶつかった可能性があります。PHP は優れていますが、大量のデータを処理するなど、一部の領域では失敗します。あなたができることがいくつかあります:

  1. 複数の php プロセスを使用してタスクを実行します (2 つのプロセスでは半分の時間がかかる可能性があります)。
  2. より高速な CPU をインストールします。
  3. 複数のマシンで処理を行います。
  4. コンパイル済み言語を使用してデータを処理する (Java、C、C++ など)
于 2012-12-05T23:02:21.840 に答える
0

この操作はオンザフライで行う必要がありますか? そうでない場合は、前処理をお勧めします...おそらくcronジョブを介して。

使用するルールを定義します。str_replace は 1 つだけですか、それともいくつかの異なるものですか? ファイル全体を一度に実行する必要がありますか? または、複数のバッチに分割できますか? (たとえば、一度にファイルの半分)

ルールが定義されたら、いつ処理を行うかを決定します。(例: 全員が仕事に着く前の午前 6 時)

その後、ジョブ キューをセットアップできます。Apache の cron ジョブを使用して、特定のタイム スケジュールで php スクリプトを実行しました。

少し前に取り組んだプロジェクトでは、次のような設定がありました。

7:00 - pull 10,000 records from mysql and write them to 3 separate files.
7:15 - run a complex regex on file one.
7:20 - run a complex regex on file two.
7:25 - run a complex regex on file three.
7:30 - combine all three files into one.
8:00 - walk into the metting with the formatted file you boss wants. *profit*

これがあなたの考えに役立つことを願っています...

于 2012-12-06T21:14:16.350 に答える
0

問題は、なぜこのスクリプトを頻繁に実行しているのかということだと思います。同じデータに対して計算 (文字列の置換) を何度も実行していますか、それとも毎回異なるデータに対して実行していますか?

答えが前者の場合、PHP 側でパフォーマンスを改善するためにできることはこれ以上ありません。より優れたハードウェア (ファイルの読み取り/書き込みを高速化するための SSD)、マルチコア CPU を使用し、データを同時に複数のスクリプトを実行して小さな断片に分割し、データを同時に処理する、およびより高速な RAM を使用するなど、他の方法でパフォーマンスを向上させることができます。 (つまり、より高速なバス速度)。

答えが後者の場合は、memcached や reddis (キー/値キャッシュ ストア) などを使用して結果をキャッシュすることを検討してください。これにより、計算を 1 回しか実行できず、メモリからの単なる線形読み取りになります。これは非常に重要です。安価で、実質的に CPU オーバーヘッドがありません (このレベルで CPU キャッシュを利用することもできます)。

PHP での文字列操作は、基本的に単なるバイト配列であるため、すでに安価です。ファイルをメモリに読み込んで文字列に格納する際の PHP のオーバーヘッドは事実上ありません。パフォーマンスの問題が発生している場所とベンチマークの数値を示すサンプル コードがあれば、より良いアドバイスが得られるかもしれませんが、現時点では、根本的なニーズに基づいてアプローチをリファクタリングする必要があるようです。

たとえば、さまざまな状況でデータを処理する場合、CPU と I/O の両方のコストを個別に考慮する必要があります。I/O はシステム コールであるため、ブロッキングを伴います。これは、CPU がそのデータの処理または計算を続行する前に、(ディスクがメモリにデータを転送している間) ネットワーク経由でさらにデータが来るのを待つ必要があることを意味します。CPU は常にメモリよりもはるかに高速であり、メモリは常にディスクよりも高速です。

違いを示す簡単なベンチマークを次に示します。

/* First, let's create a simple test file to benchmark */
file_put_contents('in.txt', str_repeat(implode(" ",range('a','z')),10000));

/* Now let's write two different tests that replace all vowels with asterisks */

// The first test reads the entire file into memory and performs the computation all at once

function test1($filename, $newfile) {
    $start = microtime(true);
    $data = file_get_contents($filename);
    $changes = str_replace(array('a','e','i','o','u'),array('*'),$data);
    file_put_contents($newfile,$changes);
    return sprintf("%.6f", microtime(true) - $start);
}

// The second test reads only 8KB chunks at a time and performs the computation on each chunk

function test2($filename, $newfile) {
    $start = microtime(true);
    $fp = fopen($filename,"r");
    $changes = '';
    while(!feof($fp)) {
        $changes .= str_replace(array('a','e','i','o','u'),array('*'),fread($fp, 8192));
    }
    file_put_contents($newfile, $changes);
    return sprintf("%.6f", microtime(true) - $start);
}

上記の 2 つのテストはまったく同じことを行いますが、少量のデータ (このテストでは約 500KB ) を使用している場合は、 Test2の方が大幅に高速であることがわかります。

実行できるベンチマークは次のとおりです...

// Conduct 100 iterations of each test and average the results
for ($i = 0; $i < 100; $i++) {
    $test1[] = test1('in.txt','out.txt');
    $test2[] = test2('in.txt','out.txt');
}
echo "Test1 average: ", sprintf("%.6f",array_sum($test1) / count($test1)), "\n",
     "Test2 average: ", sprintf("%.6f\n",array_sum($test2) / count($test2));

私にとって、上記のベンチマークはTest1 average: 0.440795とを与えますTest2 average: 0.052054。これは桁違いの違いであり、500KB のデータでテストしているだけです。ここで、このファイルのサイズを約 50MB に増やすと、反復ごとのシステム I/O 呼び出しが少なくなるため (つまり、 Test1 でメモリから線形に読み取るだけなので)、 Test1の方が実際には高速であることがわかりますが、CPU コストは高くなります (つまり、反復ごとにはるかに大きな計算を実行しています)。一般に、CPU は、I/O デバイスがバス経由で送信できるよりもはるかに大量のデータを一度に処理できることが証明されています。

そのため、ほとんどの場合、万能のソリューションではありません。

于 2012-12-06T04:20:27.950 に答える
0

あなたは Perl を知っているので、正規表現を使用して perl で文字列操作を行い、最終結果を PHP Web ページで使用することをお勧めします。

以下の理由でこちらの方が良さそうです

  1. あなたはすでにPerlを知っています
  2. Perl はより優れた文字列処理を行います

必要な場合にのみ PHP を使用できます。

于 2012-12-06T09:23:45.423 に答える