0

テキストファイルから二重引用符を削除する次の行のシェルスクリプトがあります。

sed 's/\"//g' old_file.txt > new_file.txt

^で区切られたテキストファイルから特定の列のみを選択するawkステートメントがもう1つあります。

どちらのステートメントも期待どおりに機能しています。ただし、入力ファイルのサイズが数GBを超えると、サーバーがハングします。Pythonが同じことをより効率的に行うことができるかどうか知りたいです。

アップデート:

サーバーを停止していませんが、シェルスクリプトを実行すると、同じサーバーでホストされているmysqlの速度が低下します。

4

4 に答える 4

3

Pythonがこれをより速く実行できる可能性は低いです。少しの作業で、+/-同じ効率で同じことを行うことができます。あなたがそれを間違えようとしない限り; それからそれは遅くなるからです。

sedとawkはどちらもラインモードで動作します。それらはかなりI/Oに最適化されており、それを改善できるとは思いません。Pythonスクリプトは、操作の実行に関しては高速である可能性がありますが、この場合、関連する可能性はほとんどありません

@paxdiabloが提案するようにそれらをパイプするだけです:

sed 's/"//g' old_file.txt | awk '...' > new_file.txt

または、列の形式が十分に単純な場合は、より高速な単純な形式に置き換えることができますawkcut

sed 's/"//g' old_file.txt | cut -d' ' -f1-2,4 > new_file.txt

(列1、2、4の例、スペースで区切られています)

また、中間出力が必要な場合はtee、パイプラインに入れて、その間に書き込むことができます。

sed 's/"//g' old_file.txt | tee inter_file.txt | cut -d' ' -f1-2,4 > new_file.txt

ただし、両方が同時に書き込まれるため、実際には効率が低下する可能性がありますinter_file.txtnew_file.txt


さて、私は問題が何であるかを理解したと思います。あなたの問題は、スクリプトが可能な限り速くなるため、スクリプトが十分に速くないということではありません。スループットの制限に達するのはハードドライブであるため、それを使用する他のアプリケーションは遅延します。ハードドライブには速すぎると言えます。

1つの解決策は、を使用ioniceして優先度を低くすることです。それは役立つかもしれません、それはまったく違いをもたらさないかもしれません。

ionice -c3 -p$$

現在のシェルまたはスクリプトに最低の(アイドル)I/O優先度を与えます。同様に、次を使用して、指定された優先度でスクリプトを開始できます。

ionice -c3 ./yourscript.sh

結果は、使用するI/Oスケジューラによって異なる場合があります。一部のスケジューラーはこれを無視し、一部のスケジューラーは実際にスクリプトを遅くする可能性があります(mysqlがI / Oを要求するときはいつでも)。

または、追加のプログラムを使用して、sedのスループットを制限し、効果的に速度を低下させ、mysqlが収まる空き領域を確保することもできます。ただし、最適なスループットを測定する必要があります。

そして最後に、上記のいずれもオプションではない場合は、Pythonにジャンプし、time.sleep()数百行または数千行ごとに追加して、しばらくの間スクリプトを停止し、mysqlにその仕事をさせることができます。

于 2012-08-17T08:27:04.313 に答える
1

ありそうもない。どちらも一度にファイル全体をメモリに丸呑みすることはないので、基本的に制限は何よりもディスクI/Oレートになります。

私はそれが実際にぶら下がっていない(完全に停止している)ことを収集します、はるかに可能性が高いのは、より大きなファイルを処理するのに少し時間がかかっていることです。

あなたが試すことができるいくつかのこと。

new_file.txtまず、そのファイルをプロセスに送信する場合はawk、中間ファイルをまったく作成しないでください。次のことができるはずです。

sed 's/\"//g' old_file.txt | awk 'some commands' >next_file.txt

第二に、これはかなり単純な置換であるため、一般的なケースに対応する必要があるために実行が遅くなる可能性のあるsedまたはスクリプトに依存するよりも、独自の固定フィルターを作成する方が速い場合があります。python

言い換えれば、次のようなものです。

create 1M buffer
read up to 1M from input
while not EOF:
    go through data removing `"` characters.
    write changed buffer to output.
    read up to 1M from input

適切なCコードは次のとおりです。

#include <stdio.h>
#include <errno.h>
static char buff[1000000];
int main (void) {
    int sz;
    char *src, *dst;
    while ((sz = fread (buff, 1, sizeof(buff), stdin)) > 0) {
        src = dst = buff;
        while (sz-- >= 0) {
            if (*src == '"') {
                src++;
                continue;
            }
            *dst++ = *src++;
        }
        sz = dst - buff;
        if (fwrite (buff, sz, 1, stdout) != 1) {
            fprintf (stderr, "Error %d writing data\n", errno);
            return -1;
        }
    }
    return 0;
}

これ、特定のケースの場合であるため、より高速に実行される可能性がありますが、すべての最適化と同様に、推測しないでください。

私のシステムでは、そのコードをコンパイルするとgcc -O3、最初に次の結果が得られましたsed

pax> time ( cat Photo_* | sed 's/"//g' >/dev/null )
real  0m0.094s
user  0m0.080s
sys   0m0.024s

pax> time ( cat Photo_* | sed 's/"//g' >/dev/null )
real  0m0.097s
user  0m0.076s
sys   0m0.032s

pax> time ( cat Photo_* | sed 's/"//g' >/dev/null )
real  0m0.095s
user  0m0.092s
sys   0m0.012s

pax> time ( cat Photo_* | sed 's/"//g' >/dev/null )
real  0m0.096s
user  0m0.060s
sys   0m0.048s

pax> time ( cat Photo_* | sed 's/"//g' >/dev/null )
real  0m0.095s
user  0m0.088s
sys   0m0.016s

次に、カスタマイズされたフィルターの場合:

pax> time ( cat Photo_* | ./qq >/dev/null )
real  0m0.030s
user  0m0.012s
sys   0m0.028s

pax> time ( cat Photo_* | ./qq >/dev/null )
real  0m0.032s
user  0m0.008s
sys   0m0.032s

pax> time ( cat Photo_* | ./qq >/dev/null )
real  0m0.030s
user  0m0.012s
sys   0m0.028s

pax> time ( cat Photo_* | ./qq >/dev/null )
real  0m0.030s
user  0m0.012s
sys   0m0.028s

pax> time ( cat Photo_* | ./qq >/dev/null )
real  0m0.030s
user  0m0.012s
sys   0m0.028s

したがって、平均system+userwallclock時間は次のとおりです。

         system+user   wallclock
sed           0.1056      0.0954
custom        0.0400      0.0304

したがって、カスタムフィルタを使用すると、一般的な場合よりもパフォーマンスが向上する可能性があります。テストデータの場合、そのワイルドカードに一致するファイルが3つあり、サイズ3427158は、、54624725921534あるため、合計で約14Mになります。

より大きな入力サイズの場合、3,791,657,984バイト(約4G)の単一ファイルを使用すると、sed(ユーザー+システム)時間は27.037秒で、カスタム時間は9.020秒でした(5つのサンプルの取得と平均化の両方)。


また、質問の編集に関して、このプロセスが別のより重要なプロセスの速度を低下させている場合は、そこで実行しないでください。パフォーマンスを損なうことなく、ファイルを完全に別のボックスにシフトするコマンドと、そのファイルでスクリプトを心ゆくまで実行できるコマンドの両方nice を実行する必要があります。 ionicecopy/ftp/rcp

パフォーマンスを低下させたプロダクションボックスでスクリプトを実行した場合、サイトのメンテナの半分がプレートに頭を要求することになります:-)

于 2012-08-17T08:16:43.147 に答える
1

二重引用符の削除と列の選択の両方を同時に実行して最終結果を書き出すことができるため、現在のアプローチが改善される可能性があります。このようにして、非常に大きな中間ファイルをディスクに書き込むことを回避できます。また、sedとawkをパイプに入れるよりも高速である可能性があります。これは、1つの追加プロセスと同期オーバーヘッドが必要になるためです。

于 2012-08-17T08:23:01.160 に答える
0

sedawk;の両方を使用する必要はありません。を使用するだけawkです。

3番目の列が必要だと仮定します。

awk -F'^' '{gsub(/"/, "", $3); print $3;}' huge_data_file

これには、sed、パイプライン、中間ファイルなどを必要としないなど、多くの利点があります。また、置換は"、行全体ではなく、必要な列に含まれるデータに対してのみ行われます。

私はこれを、繰り返されるこのデータで構成される64,400,000行を含む2.1Gファイルでテストしました(データの繰り返しが私のテストを汚染するかどうかはわかりません)。

"blah"^"blah blah"^"test1   ""^stuff
"blah"^"blah blah"^"test2   ""^stuff
"blah"^"blah blah"^"test3"^stuff
"blah"^"blah blah"^"test4^stuff^

$ time awk -F'^' '{gsub(/"/, "", $3); print $3;}' huge_data_file >/dev/null

real    2m31.706s
user    2m29.745s
sys 0m0.953s

「ハング」はありません。仮想メモリの使用量は105MBを超えませんでした。もちろん、実際のパフォーマンスはサーバーによって異なる場合があります。


[編集]

比較のために:

$ time sed -e 's/"//g' huge_data_file | awk -F'^' '{ print $3;}' >/dev/null

real    2m9.723s
user    3m34.355s
sys 0m3.585s

これは、2倍のメモリを必要としましたが、私の「非sed」ソリューションを効果的に魚雷で攻撃します。

于 2012-08-17T09:01:50.657 に答える