8

メモリ内で (4 バイト) 整数の大きな配列を作成して操作したいと考えています。概して、数億のオーダーを意味します。配列内の各セルは、染色体上の位置のカウンターとして機能します。必要なのは、メモリに収まり、要素に高速 (O(1)) アクセスできることだけです。私が数えているのはスパース機能ではないため、スパース配列は使用できません。

perl (少なくとも私のマシンでは) は要素ごとに 64 バイトを使用するため、通常の perl リストではこれを行うことができません。そのため、私が扱っているほとんどの生物のゲノムは大きすぎます。SQLite とハッシュ結合を介してデータをディスクに保存しようとしましたが、それらは機能しますが、特に通常のドライブでは非常に遅くなります。(4 ドライブの RAID 0 で実行すると、問題なく動作します)。

私は PDL 配列を使用できると考えました。b/c PDL は、C と同じように配列を格納し、要素ごとに 4 バイトしか使用しません。ただし、perl のリストに比べて更新速度が非常に遅いことがわかりました。

use PDL;
use Benchmark qw/cmpthese/;

my $N = 1_000_000;
my @perl = (0 .. $N - 1);
my $pdl = zeroes $N;

cmpthese(-1,{ 
    perl => sub{
        $perl[int(rand($N))]++;
    },
    pdl => sub{
        # note that I'm not even incrementing here just setting to 1
        $pdl->set(int(rand($N)), 1);
    }
});

戻り値:

          Rate  pdl perl
pdl   481208/s   -- -87%
perl 3640889/s 657%   --    

pdl set() のパフォーマンスを向上させる方法を知っている人、またはこれを達成できる別のモジュールを知っている人はいますか?

4

7 に答える 7

8

どのようなパフォーマンスが得られるかはわかりませんが、ここvecに記載されている関数を使用して、文字列をビット フィールドに分割することをお勧めします。私が実験したところ、私の Perl は最大文字数までの文字列を許容することがわかりました。これは 125,000,000 の 32 ビット値に相当します。500_000_000

my $data = "\0" x 500_000_000;
vec($data, 0, 32)++;            # Increment data[0]
vec($data, 100_000_000, 32)++;  # Increment data[100_000_000]

これで十分でない場合は、Perl のビルドに制限を制御する何かがある可能性があります。あるいは、より小さなフィールド (たとえば 16 ビット カウント) を取得できると思われる場合はvec、2 のべき乗から最大 32 までのフィールド幅を受け入れることができます。

編集:文字列サイズの制限は、32 ビット Windows プロセスでの 2GB の最大プライベート ワーキング セットに関連していると思います。Linux を実行している場合、または 64 ビットの perl を使用している場合は、私よりも幸運かもしれません。


このようなベンチマークプログラムに追加しました

my $vec = "\0" x ($N * 4);

cmpthese(-3,{ 
    perl => sub{
        $perl[int(rand($N))]++;
    },
    pdl => sub{
        # note that I'm not even incrementing here just setting to 1
        $pdl->set(int(rand($N)), 1);
    },
    vec => sub {
        vec($vec, int(rand($N)), 32)++; 
    },
});

これらの結果を与える

          Rate  pdl  vec perl
pdl   472429/s   -- -76% -85%
vec  1993101/s 322%   -- -37%
perl 3157570/s 568%  58%   --

そのためvec、ネイティブ アレイの 3 分の 2 の速度で使用できます。おそらくそれは許容されます。

于 2012-03-16T02:05:14.507 に答える
7

必要な PDL コマンドはindadd. (別の場所で指摘してくれた PDL Pumpking の Chris Marshall に感謝します。)

PDL は、私が「ベクトル化された」操作と呼ぶもののために設計されています。C 操作と比較して、Perl 操作は非常に遅いため、PDL メソッド呼び出しの数を最小限に抑え、各呼び出しで多くの作業を実行する必要があります。たとえば、このベンチマークでは、一度に実行する更新の数を (コマンドライン パラメータとして) 指定できます。perl 側はループする必要がありますが、PDL 側は 5 つほどの関数呼び出ししか実行しません。

use PDL;
use Benchmark qw/cmpthese/;

my $updates_per_round = shift || 1;

my $N = 1_000_000;
my @perl = (0 .. $N - 1);
my $pdl = zeroes $N;

cmpthese(-1,{ 
    perl => sub{
        $perl[int(rand($N))]++ for (1..$updates_per_round);
    },
    pdl => sub{
        my $to_update = long(random($updates_per_round) * $N);
        indadd(1,$to_update,$pdl);
    }
});

これを引数 1 で実行すると、 を使用した場合よりもさらにパフォーマンスが低下しますset。これは予想どおりです。

$ perl script.pl 1
          Rate   pdl  perl
pdl    21354/s    --  -98%
perl 1061925/s 4873%    --

それは補うべき多くの根拠です!しかし、そこで我慢してください。ラウンドごとに 100 回の反復を行うと、改善が得られます。

$ perl script.pl 100
        Rate  pdl perl
pdl  16906/s   -- -18%
perl 20577/s  22%   --

また、1 ラウンドあたり 10,000 回の更新で、PDL は Perl よりも 4 倍優れています。

$ perl script.pl 10000
      Rate perl  pdl
perl 221/s   -- -75%
pdl  881/s 298%   --

PDL は、値がさらに大きい場合でも、プレーンな Perl よりも約 4 倍速く実行し続けます。

より複雑な操作では、PDL のパフォーマンスが低下する可能性があることに注意してください。これは、PDL が中間操作のために大規模で一時的なワークスペースを割り当てて破棄するためです。その場合は、 の使用を検討してInline::Pdlppください。ただし、これは初心者向けのツールではないため、自分にとって本当に最適なツールであると判断するまで、そこに飛び込まないでください。

このすべてに代わる別の方法は、次のInline::Cように使用することです。

use PDL;
use Benchmark qw/cmpthese/;

my $updates_per_round = shift || 1;

my $N = 1_000_000;
my @perl = (0 .. $N - 1);
my $pdl = zeroes $N;
my $inline = pack "d*", @perl;
my $max_PDL_per_round = 5_000;

use Inline 'C';

cmpthese(-1,{ 
    perl => sub{
        $perl[int(rand($N))]++ for (1..$updates_per_round);
    },
    pdl => sub{
        my $to_update = long(random($updates_per_round) * $N);
        indadd(1,$to_update,$pdl);
    },
    inline => sub{
        do_inline($inline, $updates_per_round, $N);
    },
});


__END__

__C__

void do_inline(char * packed_data, int N_updates, int N_data) {
    double * actual_data = (double *) packed_data;
    int i;
    for (i = 0; i < N_updates; i++) {
        int index = rand() % N_data;
        actual_data[index]++;
    }
}

私にとって、Inline 関数は一貫して Perl と PDL の両方に勝っています。の大きな値$updates_per_round、たとえば 1,000 の場合、Inline::Cのバージョンは、純粋な Perl よりも約 5 倍速く、PDL よりも 1.2 倍から 2 倍速くなります。が 1 の場合でも$updates_per_round、Perl が PDL を簡単に打ち負かしますが、インライン コードは Perl コードよりも 2.5 倍高速です。

これだけを達成する必要がある場合は、 を使用することをお勧めしますInline::C

ただし、データに対して多くの操作を実行する必要がある場合は、そのパワー、柔軟性、およびパフォーマンスのために PDL を使用するのが最善です。vec()PDL データの使用方法については、以下を参照してください。

于 2012-03-22T17:02:26.617 に答える
4

PDL::set()そしてPDL::get()何よりも学習補助として意図されています。これらは、PDL変数にアクセスするための悲観的な方法を構成します。組み込みのバルクアクセスルーチンのいくつかを使用する方がはるかに良いでしょう。PDLコンストラクター自体はPerlリストを受け入れます。

$pdl = pdl(@list)

適度に高速です。を使用してASCIIファイルから直接データをロードすることもPDL::rcols、多くのIOルーチンの1つを使用してバイナリファイルからデータをロードすることもできます。マシン順にパックされた文字列としてデータがある場合は、PDLメモリに直接アクセスすることもできます。

$pdl = PDL->new_from_specification(long,$elements);
$dr = $pdl->get_dataref;
$$dr = get_my_packed_string();
$pdl->upd_data;

また、PDLオブジェクトを使用して整数の配列を保持したりindadd、データを大規模に操作するためのPDL計算(など)を使用したりすることで、「ケーキを持って食べる」こともできますがvec()、PDLデータを直接使用することもできます。メソッドを介して取得できる文字列get_dataref

vec($$dr,int(rand($N)),32);

bswap4リトルエンディアンシステムを使用している場合は、データが必要になります。

$pdl->bswap4;
$dr = $pdl->get_dataref;
vec($$dr,int(rand($N)),32)++;
$pdl->upd_data;
$pdl->bswap4;

え、出来上がり!

于 2012-04-18T14:19:25.620 に答える
2

PDLは、操作をスレッド化できる場合に優先されますが、ランダムアクセスと割り当て用に最適化されていないようです。おそらく、PDLの知識が豊富な人が役立つでしょう。

于 2012-03-16T01:40:57.140 に答える
2

CPAN のPacked::Arrayが役立つかもしれません。

Packed::Array は、パックされた符号付き整数配列クラスを提供します。Packed::Array を使用して構築された配列は、プラットフォーム固有の整数と一致する符号付き整数のみを保持できますが、それらの整数を保持するために実際に必要なだけのメモリしか必要としません。したがって、32 ビット システムの場合、配列エントリごとに約 20 バイトではなく、わずか 4 バイトで済みます。

于 2012-03-16T04:52:46.053 に答える
2

整数を使用していたため、染色体での使用には問題ないはずです。これを試してください

use PDL;
use Benchmark qw/cmpthese/;

my $N =  1_000_000;
my @perl;
@perl = (0 .. $N - 1);
my $pdl;
$pdl = (zeroes($N));

cmpthese(-1,{ 
perl => sub{
    $perl[int(rand($N))]++;
},
pdl2 => sub{
    # note that I'm not even incrementing here just setting to 1
    $pdl->set(int(rand($N)), 1);
    $pdl2 = pack "w*", $pdl;
}
});

そして、私がこれから得たアウトプットは...

           Rate  pdl2  perl
pdl2   46993/s    --  -97%
perl 1641607/s 3393%    --

これは、得た 2 セントを追加せずにこのコードを最初に試したときとの大きなパフォーマンスの違いを示しています。

          Rate  pdl perl
pdl   201972/s   -- -86%
perl 1472123/s 629%   -- 
于 2012-04-18T17:29:32.533 に答える
0

私の上記の答えは価値がないかもしれません...これは助けになるかもしれません..

 use PDL;
$x = sequence(45000,45000);

16GBのRAMを持っていて使用しない限り、これは機能しません

$PDL::BIGPDL=1;
于 2013-05-12T05:28:53.127 に答える