3

私はAoAのハッシュを持っています:

$hash{$key} = [ 
               [0.0,1.0,2.0],
               10.0,
               [1.5,9.5,5.5],
              ];

次のようにクランチする必要があります。

$err += (($hash{$key}[0][$_]-$hash{key}[2][$_])*$hash{$key}[1])**2 foreach (0 .. 2);

2 つの配列間の二乗加重差を計算します。私のハッシュは大きいので、PDL が計算の高速化に役立つことを期待していましたが、何らかの理由でそうではありません。私はまだPDLに慣れていないので、おそらく何かを台無しにしています。以下の PDL を使用したスクリプトは、最大 10 倍遅くなります。説明: 次の 2 つのスクリプトは、私のプログラムで何が起こっているかを簡単に表現しようとしたものです。いくつかの参照値をハッシュに読み込み、(その場でハッシュに取り込まれた) 観測値とそれらの値を重み付けして何度も比較します。スクリプトでは、参照配列、重み、観測配列を任意の固定値に設定しましたが、実行時にはそうではありません。

PDL を使用しない場合と使用する場合の 2 つの単純なスクリプトを次に示します。

PDLなし

use strict;
use warnings;
use Time::HiRes qw(time);

my $t1 = time;
my %hash;
my $error = 0;

foreach (0 .. 10000){
  $hash{$_} = [
               [0.000, 1.000, 2.0000],
               10.0,
               [1.5,9.5,5.5],
              ];
  foreach my $i (0 .. 2){
    $error += (($hash{$_}[0][$i]-$hash{$_}[2][$i])*$hash{$_}[1])**2;
  }
}

my $t2 = time;

printf ( "total time: %10.4f error: %10.4f\n", $t2-$t1,$error);

PDL付き

use strict;
use warnings;
use PDL;
use Time::HiRes qw(time);

my $t1 = time;
my %hash;
my $error = 0;

foreach (0 .. 10000){
  $hash{$_}[0] = pdl[0.000, 1.000, 2.0000];
  $hash{$_}[1] = pdl[10.0];
  $hash{$_}[2] = pdl[1.5,9.5,5.5];
  my $e = ($hash{$_}[0]-$hash{$_}[2])*$hash{$_}[1];
  $error += inner($e,$e);
}

my $t2 = time;

printf ( "total time: %10.4f error: %10.4f\n", $t2-$t1, $error);
4

4 に答える 4

5

PDL は、配列計算を処理するように最適化されています。データにハッシュを使用していますが、キーは数値であるため、PDL 配列オブジェクトに関して再定式化し、パフォーマンスを大幅に向上させることができます。次のサンプル コードのすべての PDL バージョンは、 PDL コードを使用しないオリジナルよりも約36 倍高速に実行されます (PDL コードを使用たオリジナルよりも300 倍高速に実行されます)。

すべての PDL

use strict;
use warnings;
use PDL;
use Time::HiRes qw(time);

my $t1 = time;
my %hash;
my $error = 0;

my $pdl0 = zeros(3,10001);  # create a [3,10001] pdl
$pdl0 .= pdl[0.000, 1.000, 2.0000];

my $pdl1 = zeros(1,10001);  # create a [1,10001] pdl
$pdl1 .= pdl[10.0];

my $pdl2 = zeros(3,10001);  # create a [3,10001] pdl
$pdl2 .= pdl[1.5,9.5,5.5];

my $e = ($pdl0 - $pdl2)*$pdl1;
$error = sum($e*$e);

my $t2 = time;

printf ( "total time: %10.4f error: %10.4f\n", $t2-$t1, $error);

計算に PDL を使用するための詳細なイントロについては、 PDL ブックを参照してください。PDL ホームページは、PDL に関するすべての出発点としても最適です。

于 2012-04-22T15:45:08.967 に答える
3

私はあなたのコードを数回リファクタリングしました。最初はできるだけ複雑さをループの外に移動しました。次に、抽象化のレイヤーなどを削除しました。これにより、式が大幅に簡略化され、同じ結果を維持しながら、システムのランタイムが約60%削減されました。

use Modern::Perl;
use Time::HiRes qw(time);

my $t1 = time;
my $error = 0;

my @foo = ( 0.000, 1.000, 2.0000 );
my $bar = 10.0;
my @baz = ( 1.5, 9.5, 5.5 );

foreach ( 0 .. 10000 ) {
    $error += ( ( $foo[$_] - $baz[$_] ) * $bar )**2 for 0 .. 2
}

my $t2 = time;

printf ( "total time: %10.4f error: %10.4f\n", $t2-$t1,$error);

これは単なる古いPerlです。PDLはありません。うまくいけば、これはあなたのプロジェクトに役立つでしょう。

ちなみに、コードのセクションの実行にかかる時間を計算するとき、私はたまたま、、、、および関数を備えたベンチマークモジュールを好みます。あなたはそれからより多くの情報を得る。timethis()timethese()cmpthese()

于 2011-06-19T08:31:53.347 に答える
3

まず、配列が大きくない限り、PDL はあまり役に立ちません。では、0 から 10000 までのインデックスが付いたハッシュを使用する代わりに、それぞれ (基本的に) 7 つのスカラー要素を持つハッシュを使用する代わりに、それぞれ 10001 要素の 7 つの PDL ベクトルを作成し、ベクトル操作を使用してそれらを操作できますか?

次に、式$hash{$_}は名前を付けるたびに評価されるため、除外する必要があります。たとえば、標準の Perl コードでは、次のようにする必要があります。

my $vec = $hash{$_};
foreach my $i (0 .. 2){
    $error += (($vec->[0][$i]-$vec->[2][$i])*$vec->[1])**2;
}
于 2011-06-19T04:48:42.370 に答える
0

Nemo の提案に基づいて、速度がわずかに向上する PDL スクリプトを次に示します。私はまだ PDL グリーンなので、おそらくもっと良い方法があります。また、ハッシュへの値の追加を参照/重みと観測のループに分割して、OP をより大きなプログラムで起こっていることのようにします。上記の「説明」を参照してください。

use strict;
use warnings;
use PDL;
use PDL::NiceSlice;
use Time::HiRes qw(time);

my $t1 = time;
my %hash;
my $nvals=10000;

#construct hash of references and weights
foreach (0 .. $nvals){
  $hash{$_} = [
                 [0.000, 1.000, 2.0000],
                 [10.0, 10.0, 10.0],
               ];
}

#record observations
foreach (0 .. $nvals){
  $hash{$_}[2] = [1.5,9.5,5.5]; 
}

my $tset = time;

my @ref;
my @obs;
my @w;

foreach (0 .. $nvals){
  my $mat = $hash{$_};
  push @ref, @{$mat->[0]};
  push @w,   @{$mat->[1]};
  push @obs, @{$mat->[2]};
}

my $ref = pdl[@ref];
my $obs = pdl[@obs];
my $w   = pdl[@w];

my $diff = (($ref-$obs)*$w)**2;
my $error = sum($diff);

my $t2 = time;

printf ( "$nvals time setup: %10.4f crunch: %10.4f total: %10.4f error: %10.4f\n", $tset-$t1,$t2-$tset, $t2-$t1,$error);
于 2011-06-19T17:43:17.843 に答える