私はベクターが大好きです。彼らは気の利いた、速いです。しかし、私はvalarrayと呼ばれるものが存在することを知っています。なぜベクトルではなくvalarrayを使用するのですか?valarrayには構文上の砂糖があることは知っていますが、それ以外の場合、いつ役立つのですか?
私はベクターが大好きです。彼らは気の利いた、速いです。しかし、私はvalarrayと呼ばれるものが存在することを知っています。なぜベクトルではなくvalarrayを使用するのですか?valarrayには構文上の砂糖があることは知っていますが、それ以外の場合、いつ役立つのですか?
回答:
Valarrays(値配列)は、Fortranの速度の一部をC ++にもたらすことを目的としています。ポインターのvalarrayを作成しないので、コンパイラーはコードに関する仮定を行い、コードを最適化できます。(Fortranが非常に高速である主な理由は、ポインター型がないため、ポインターのエイリアシングが発生しないためです。)
また、Valarraysには、かなり簡単な方法でそれらをスライスできるクラスがありますが、標準のその部分はもう少し作業を使用できます。それらのサイズ変更は破壊的であり、反復子がありません。
そのため、作業している数値であり、利便性がそれほど重要でない場合は、valarrayを使用してください。それ以外の場合、ベクターははるかに便利です。
valarray
間違った場所に間違った時期に生まれた孤児のようなものです。それは最適化の試みであり、かなり具体的には、それが書かれたときに強力な数学に使用されたマシン、具体的には、クレイのようなベクトルプロセッサです。
ベクトルプロセッサの場合、一般的にやりたいことは、配列全体に単一の操作を適用し、次に配列全体に次の操作を適用し、その後、必要なすべてを実行するまで続けます。
ただし、かなり小さな配列を扱っている場合を除き、キャッシュではうまく機能しない傾向があります。最近のほとんどのマシンでは、一般に(可能な範囲で)アレイの一部をロードし、それに対してすべての操作を実行してから、アレイの次の部分に移動することをお勧めします。
valarray
また、エイリアシングの可能性を排除することになっています。これにより、(少なくとも理論的には)レジスタに値を格納する方が自由になるため、コンパイラの速度が向上します。しかし実際には、実際の実装がこれをかなりの程度まで活用しているとは、私にはまったく確信がありません。私はそれがむしろ鶏と卵の種類の問題であると思います-コンパイラのサポートなしではそれはポピュラーになりませんでした、そしてそれがポピュラーでない限り、誰もそれをサポートするためにコンパイラで作業する問題に行くつもりはありません。
valarrayで使用する、戸惑う(文字通り)補助クラスの配列もあります。あなたが得るslice
、slice_array
、gslice
とgslice_array
の作品をプレイするためにvalarray
、それは多次元配列のように動作します。またmask_array
、操作を「マスク」することもできます(たとえば、xからyに項目を追加しますが、zがゼロ以外の位置でのみ)。を簡単に使用するにはvalarray
、これらの補助的なクラスについて多くを学ぶ必要があります。その一部はかなり複雑で、どれも(少なくとも私には)十分に文書化されているようには見えません。
結論:それは輝きの瞬間を持ち、いくつかのことをかなりきれいに行うことができますが、それが不明瞭である(そしてほぼ確実に残る)非常に良い理由もいくつかあります。
編集(8年後、2017年):上記の一部は、少なくともある程度は古くなっています。一例として、インテルはコンパイラー用に最適化されたバージョンのvalarrayを実装しました。Intel Integrated Performance Primitives(Intel IPP)を使用してパフォーマンスを向上させます。正確なパフォーマンスの向上は間違いなく異なりますが、シンプルなコードを使用したクイックテストでは、の「標準」実装でコンパイルされた同一のコードと比較して、速度が約2:1向上していることがわかりますvalarray
。
したがって、C ++プログラマーがvalarray
大量に使用し始めるとは完全には確信していませんが、速度を向上できる状況が少なくともあります。
C ++ 98の標準化中に、valarrayは、ある種の高速数学計算を可能にするように設計されました。ただし、その頃、Todd Veldhuizenは式テンプレートを発明し、blitz ++を作成しました。同様のテンプレートメタ技術が発明され、標準がリリースされる前にvalarrayがかなり時代遅れになりました。valarrayの最初の提案者であるIIRCは、標準化の途中でそれを放棄しましたが、これは(真の場合)助けにはなりませんでした。
ISTRが標準から削除されなかった主な理由は、誰も時間をかけて問題を徹底的に評価し、削除する提案を書いていないためです。
ただし、これらはすべて漠然と記憶された伝聞であることに注意してください。これを塩の粒と一緒に取り、誰かがこれを修正または確認することを望みます。
valarrayには構文糖があることを知っています
私はstd::valarrays
構文糖に関してはそれほど多くはないと思います。構文は異なりますが、違いを「砂糖」とは呼びません。APIは奇妙です。C ++プログラミング言語std::valarray
のs のセクションでは、この異常なAPIと、sは高度に最適化されることが期待されているため、使用中に表示されるエラーメッセージはおそらく直感的ではないという事実に言及しています。std::valarray
好奇心から、約1年前に私はstd::valarray
対戦しましたstd::vector
。コードや正確な結果はもうありません(ただし、独自のコードを書くことは難しくありません)。GCC私が使用していた使用している場合、ほとんどのパフォーマンス上の利点を得るstd::valarray
私の実装は、標準偏差を計算するための簡単な数学のためではなく(もちろん、及び、標準偏差はない複雑なこと、限り数学が行くように)。 大規模な各アイテムの(注、からアドバイスを以下musiphil、私はからほぼ同じパフォーマンスを得ることができたstd::vector
操作は、std::valarray
sの操作よりもキャッシュの方が適していると思います。vector
とvalarray
)。
結局、std::vector
メモリ割り当てや一時的なオブジェクト作成などに細心の注意を払いながら使うことにしました。
双方std::vector
とstd::valarray
連続したブロックにデータを格納します。ただし、彼らはさまざまなパターンを使用してそのデータにアクセスします。さらに重要なのは、API for std::valarray
は、API forとは異なるアクセスパターンを推奨しますstd::vector
。
標準偏差の例では、特定のステップで、コレクションの平均と、各要素の値と平均との差を見つける必要がありました。
についてstd::valarray
、私は次のようなことをしました:
std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> temp(mean, original_values.size());
std::valarray<double> differences_from_mean = original_values - temp;
std::slice
またはでより賢くなったかもしれませんstd::gslice
。もう5年以上になります。
のためにstd::vector
、私は次のように何かをしました:
std::vector<double> original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();
std::vector<double> differences_from_mean;
differences_from_mean.reserve(original_values.size());
std::transform(original_values.begin(), original_values.end(), std::back_inserter(differences_from_mean), std::bind1st(std::minus<double>(), mean));
今日、私は確かにそれを違うように書くでしょう。他に何もなければ、私はC ++ 11ラムダを利用します。
これら2つのコードスニペットが異なることをするのは明らかです。まず、このstd::vector
例は、std::valarray
例のように中間コレクションを作成しません。しかし、私は違いが間の違いに結びついているので、それはそれらを比較するために公正だと思うstd::vector
とstd::valarray
。
この回答を書いたとき、2つstd::valarray
のs(std::valarray
例の最後の行)から要素の値を減算すると、例の対応する行std::vector
(たまたま最後の行)よりもキャッシュに適さないのではないかと思いました。
ただし、
std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> differences_from_mean = original_values - mean;
std::vector
例と同じことを行い、パフォーマンスはほとんど同じです。結局、問題はあなたがどちらのAPIを好むかということです。
std::vector
がキャッシュよりもでうまく機能する理由は考えられませんstd::valarray
。どちらも、要素に単一の連続したメモリブロックを割り当てます。
valarray
上記の例では、構築するために持っていなかったtemp
valarray
オブジェクトを、あなただけ行っている可能性std::valarray<double> differences_from_mean = original_values - mean;
、およびキャッシュの振る舞いは、のそれに類似していなければならないvector
例。(ちなみに、mean
そうint
である場合double
、そうでない場合は、必要になる可能性がありstatic_cast<double>(mean)
ます。)
valarray
。それによってパフォーマンスが向上するかどうかを確認する必要があります。ためのようmean
であることint
:それは間違いでした。私は最初にint
s を使用して例を記述しましたmean
が、その後、切り捨てが原因で実際の平均から非常に離れていることに気付きました。しかし、最初の編集で必要ないくつかの変更を見逃しました。
valarrayは、一部のFORTRANベクトル処理の良さをC ++でこすり落とすことになっています。どういうわけか、必要なコンパイラのサポートは実際には起こりませんでした。
Josuttisの本には、valarray(こことここ)に関する興味深い(いくぶん非難する)コメントが含まれています。
ただし、Intelは最近のコンパイラリリースでvalarrayを再検討しているようです(たとえば、スライド9を参照)。これは、4ウェイSIMD SSE命令セットが8ウェイAVXおよび16ウェイLarrabee命令によって結合されようとしていることを考えると、興味深い開発です。移植性の観点から、 (たとえば)組み込みよりもvalarray。
私はvalarrayの1つの良い使い方を見つけました。numpy配列のようにvalarrayを使用することです。
auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);
上記はvalarrayで実装できます。
valarray<float> linspace(float start, float stop, int size)
{
valarray<float> v(size);
for(int i=0; i<size; i++) v[i] = start + i * (stop-start)/size;
return v;
}
std::valarray<float> arange(float start, float step, float stop)
{
int size = (stop - start) / step;
valarray<float> v(size);
for(int i=0; i<size; i++) v[i] = start + step * i;
return v;
}
string psstm(string command)
{//return system call output as string
string s;
char tmp[1000];
FILE* f = popen(command.c_str(), "r");
while(fgets(tmp, sizeof(tmp), f)) s += tmp;
pclose(f);
return s;
}
string plot(const valarray<float>& x, const valarray<float>& y)
{
int sz = x.size();
assert(sz == y.size());
int bytes = sz * sizeof(float) * 2;
const char* name = "plot1";
int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, bytes);
float* ptr = (float*)mmap(0, bytes, PROT_WRITE, MAP_SHARED, shm_fd, 0);
for(int i=0; i<sz; i++) {
*ptr++ = x[i];
*ptr++ = y[i];
}
string command = "python plot.py ";
string s = psstm(command + to_string(sz));
shm_unlink(name);
return s;
}
また、Pythonスクリプトが必要です。
import sys, posix_ipc, os, struct
import matplotlib.pyplot as plt
sz = int(sys.argv[1])
f = posix_ipc.SharedMemory("plot1")
x = [0] * sz
y = [0] * sz
for i in range(sz):
x[i], y[i] = struct.unpack('ff', os.read(f.fd, 8))
os.close(f.fd)
plt.plot(x, y)
plt.show()
C ++ 11標準は次のように述べています。
valarray配列クラスは、特定の形式のエイリアシングがないように定義されているため、これらのクラスの操作を最適化できます。
C ++ 11 26.6.1-2を参照してください。