乱数を含む1GBのテキストファイルを生成する最速の方法は何ですか?

乱数を含む1GBのテキストファイルを生成する最速の方法は何ですか?

Bashスクリプトを試しましたが、単純な1MBファイルを生成するのに時間がかかりすぎました。/dev/randomorを使用すると答えが出てくるようですが/dev/urandom、ここの他の記事はこれらのものを使ってファイルにさまざまなデータを追加する方法だけを示していますが、私は数字だけを追加したいと思います。

それでは、サイズが1 GBで、0から9までの数字のみを含む任意のファイルを作成するために使用できるコマンドはありますか?

編集:出力が次のようになります。

0 1 4 7 ..... 9
8 7 5 8 ..... 8
....
....
8 7 5 3 ..... 3

範囲は0〜9で、数字0、1、2、3、4、5、6、7、8、9のみを意味します。また、スペースで区切られ、1行に100行、最大n行数が必要です。このnは私が気にしない部分です。最終サイズを1GBに設定したいです。

編集:Ubuntu 16.04 LTSを使用しています。

ベストアンサー1

これ:

 LC_ALL=C tr '\0-\377' \
             '[0*25][1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][x*]' \
    < /dev/urandom |
    tr -d x |
    fold -w 1 |
    paste -sd "$(printf '%99s\\n')" - |
    head -c1G

headサポートされている実装を想定-c)私のシステムではかなり速いようです。

trフルバイト範囲(0から255、8進数で0から0377)を変換します。最初の25バイトは0、次の25バイトは1です。それから25 9で、残りの部分(250から255)は「x」ですtr -d x/dev/urandomします。

これはバイトの97%に対して1桁の数字を生成します/dev/urandomfold -w 11行に1桁ずつ作成します。paste -s呼び出しは99個の空白文字と改行文字で構成される区切り文字のリストを使用して行われるため、各行にはスペースで区切られた100個の数字があります。

head -c1Gそのうちの最初のGiB(2 30)を取得します。最後の行は切り捨てられ、制限はありません。 2 30 -1 に切り捨てられ、行方不明の改行を手動で追加したり、200 バイト行の 5,000 万に相当する 10 9バイトのローザを作成したりできます(head -n 50000000これも標準/移植可能なコマンドです)。

zshクアッドコアシステムから得られるこれらのタイミングは、CPU時間が消費される場所を示す。

LC_ALL=C tr '\0-\377'  < /dev/urandom  0.61s user 31.28s system 99% cpu 31.904 total
tr -d x  1.00s user 0.27s system 3% cpu 31.903 total
fold -w 1  14.93s user 0.48s system 48% cpu 31.902 total
paste -sd "$(printf '%99s\\n')" -  7.23s user 0.08s system 22% cpu 31.899 total
head -c1G > /dev/null  0.49s user 1.21s system 5% cpu 31.898 total

最初trはボトルネックです。ほとんどの時間はカーネルで消費されます(乱数生成のため)。時間はバイトを取得できる速度とほぼ一致します/dev/uramdom(約19MiB / s、ここでは32MiB / sで/ dev / urandomの0.97バイトごとに2バイトを生成します)。fold各バイトの後に改行を挿入するだけでも無理なCPU時間(15秒)がかかるようですが、私の場合は別のCPUで動作したため、全体の時間には影響しません(該当-bオプションを追加して作成したものです。効率的でdd cbs=1 conv=unblockより良い選択のようです)。

サブシェルでhead -c1Gファイルサイズの制限を設定することで(他のほとんどのシェル(含む)を使用またはlimit filesize 1024m使用zsh)オーバーライドulimit -f "$((1024*1024))"し、数秒を節約できます。zsh

各バイトに対して2桁を抽出すると状況が改善される可能性がありますが、他のアプローチを取る必要があります。上記のコードは、tr256バイトの配列ですべてのバイトを見つけるだけでよいため、非常に効率的です。一度に2バイトに対してこれを行うことはできず、よりhexdump -e '1/1 "%02u"'複雑なアルゴリズムを使用して同様のアプローチを使用してバイトのテキスト表現を計算することは、乱数生成自体よりもコストがかかります。しかし、私の場合のように、利用可能なCPUコアがあれば、まだ数秒を節約できます。

そして:

< /dev/urandom LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -n250000000 -ve '500/1 "%02u" "\n"' |
  fold -w1 |
  paste -sd "$(printf '%99s\\n')" - > /dev/null

私は次のような結果を得ます(ただし、1,073,741,824ではなく1,000,000,000バイトであることに注意してください)。

LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' < /dev/urandom  0.32s user 18.83s system 70% cpu 27.001 total
tr -d x  2.17s user 0.09s system 8% cpu 27.000 total
hexdump -n250000000 -ve '500/1 "%02u" "\n"'  26.79s user 0.17s system 99% cpu 27.000 total
fold -w1  14.42s user 0.67s system 55% cpu 27.000 total
paste -sd "$(printf '%99s\\n')" - > /dev/null  8.00s user 0.23s system 30% cpu 26.998 total

全体的にCPU時間は多くなりますが、4つのCPUコアに分散しているため、壁時計の時間が少なくなります。これでボトルネックが発生しますhexdump

dd代わりに、行ベースを使用すると、fold実際に実行する必要がある作業量を減らし、hexdumpCPU間の作業バランスを向上させることができます。

< /dev/urandom LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -ve '"%02u"' |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -

(ここではGNUddiflag=fullblockGNUを仮定status=none)、以下を提供します:

LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' < /dev/urandom  0.32s user 15.58s system 99% cpu 15.915 total
tr -d x  1.62s user 0.16s system 11% cpu 15.914 total
hexdump -ve '"%02u"'  10.90s user 0.32s system 70% cpu 15.911 total
dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock  5.44s user 0.19s system 35% cpu 15.909 total
paste -sd "$(printf '%99s\\n')" - > /dev/null  5.50s user 0.30s system 36% cpu 15.905 total

乱数生成のボトルネックに戻ります。

@OleTangeが指摘したように、そのユーティリティがあれば、opensslそれを使用してより高速な(特にAES命令を持つプロセッサで)疑似ランダムバイトジェネレータを取得できます。

</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom

私のシステムから1秒あたりに噴出されるバイト数はです/dev/urandom。 (比較することはできません。)暗号化方式で安全なランダムソースこれがあなたのユースケースに適用される場合)。

</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom 2> /dev/null | 
  LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  hexdump -ve '"%02u"' |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -

今与えられた:

openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom < /dev/zero 2>   1.13s user 0.16s system 12% cpu 10.174 total
LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]'  0.56s user 0.20s system 7% cpu 10.173 total
tr -d x  2.50s user 0.10s system 25% cpu 10.172 total
hexdump -ve '"%02u"'  9.96s user 0.19s system 99% cpu 10.172 total
dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock  4.38s user 0.20s system 45% cpu 10.171 total
paste -sd "$(printf '%99s\\n')" - > /dev/null

hexdumpボトルネックに戻ります。

まだ空きCPUがあるため、そのうち3つを並列に実行できますhexdump

</dev/zero openssl enc -aes-128-ctr -nosalt -pass file:/dev/urandom 2> /dev/null | 
  LC_ALL=C tr '\0-\377' '\0-\143\0-\143[x*]' |
  tr -d x |
  (hexdump -ve '"%02u"' <&3 & hexdump -ve '"%02u"' <&3 & hexdump -ve '"%02u"') 3<&0 |
  dd bs=50000 count=10000 iflag=fullblock status=none cbs=1 conv=unblock |
  paste -sd "$(printf '%99s\\n')" -

(これはバックグラウンドで実行されたときに/ dev / nullのコマンドを閉じるstdinを<&3除くすべてのシェルに必要です。)zsh

これで6.2秒に短縮され、CPUがほぼ完全に活用されます。

おすすめ記事