C ++ valarray対vector


159

私はベクターが大好きです。彼らは気の利いた、速いです。しかし、私はvalarrayと呼ばれるものが存在することを知っています。なぜベクトルではなくvalarrayを使用するのですか?valarrayには構文上の砂糖があることは知っていますが、それ以外の場合、いつ役立つのですか?


2
先日もこれを考えていました。私の知る限り、これはまさに特殊な数学ベクトルと同じです。
GManNickG

valarrayは式テンプレートを実行しませんか?
Mooing Duck 2013

物理学者ウルリッヒムツェは、valarray ここここ
ライフバランスのユース

回答:


70

Valarrays(値配列)は、Fortranの速度の一部をC ++にもたらすことを目的としています。ポインターのvalarrayを作成しないので、コンパイラーはコードに関する仮定を行い、コードを最適化できます。(Fortranが非常に高速である主な理由は、ポインター型がないため、ポインターのエイリアシングが発生しないためです。)

また、Valarraysには、かなり簡単な方法でそれらをスライスできるクラスがありますが、標準のその部分はもう少し作業を使用できます。それらのサイズ変更は破壊的であり、反復子がありません。

そのため、作業している数値であり、利便性がそれほど重要でない場合は、valarrayを使用してください。それ以外の場合、ベクターははるかに便利です。


11
ポインタを回避するようには設計されていません。C ++ 11は、反復子を返すvalarrayのbegin()およびend()を定義しています
Mohamed El-Nakib '8/02/08

3
@ user2023370:これが、多くのFortranユーザーがFortran 77を好む理由です。:)
Michael

152

valarray間違った場所に間違った時期に生まれた孤児のようなものです。それは最適化の試みであり、かなり具体的には、それが書かれたときに強力な数学に使用されたマシン、具体的には、クレイのようなベクトルプロセッサです。

ベクトルプロセッサの場合、一般的にやりたいことは、配列全体に単一の操作を適用し、次に配列全体に次の操作を適用し、その後、必要なすべてを実行するまで続けます。

ただし、かなり小さな配列を扱っている場合を除き、キャッシュではうまく機能しない傾向があります。最近のほとんどのマシンでは、一般に(可能な範囲で)アレイの一部をロードし、それに対してすべての操作を実行してから、アレイの次の部分に移動することをお勧めします。

valarrayまた、エイリアシングの可能性を排除することになっています。これにより、(少なくとも理論的には)レジスタに値を格納する方が自由になるため、コンパイラの速度が向上します。しかし実際には、実際の実装がこれをかなりの程度まで活用しているとは、私にはまったく確信がありません。私はそれがむしろ鶏と卵の種類の問題であると思います-コンパイラのサポートなしではそれはポピュラーになりませんでした、そしてそれがポピュラーでない限り、誰もそれをサポートするためにコンパイラで作業する問題に行くつもりはありません。

valarrayで使用する、戸惑う(文字通り)補助クラスの配列もあります。あなたが得るsliceslice_arraygslicegslice_arrayの作品をプレイするためにvalarray、それは多次元配列のように動作します。またmask_array、操作を「マスク」することもできます(たとえば、xからyに項目を追加しますが、zがゼロ以外の位置でのみ)。を簡単に使用するにはvalarray、これらの補助的なクラスについて多くを学ぶ必要があります。その一部はかなり複雑で、どれも(少なくとも私には)十分に文書化されているようには見えません。

結論:それは輝きの瞬間を持ち、いくつかのことをかなりきれいに行うことができますが、それが不明瞭である(そしてほぼ確実に残る)非常に良い理由もいくつかあります。

編集(8年後、2017年):上記の一部は、少なくともある程度は古くなっています。一例として、インテルはコンパイラー用に最適化されたバージョンのvalarrayを実装しました。Intel Integrated Performance Primitives(Intel IPP)を使用してパフォーマンスを向上させます。正確なパフォーマンスの向上は間違いなく異なりますが、シンプルなコードを使用したクイックテストでは、の「標準」実装でコンパイルされた同一のコードと比較して、速度が約2:1向上していることがわかりますvalarray

したがって、C ++プログラマーがvalarray大量に使用し始めるとは完全には確信していませんが、速度を向上できる状況が少なくともあります。


1
valarray内に任意のオブジェクトタイプを格納することは特に禁止されていますか?
user541686

6
@Mehrdad:はい-[Numeric.Requirements]に(かなり長い)制限のリストがあります。ほんのいくつかの例では、すべての抽象クラスと例外が禁止されています。また、(たとえば)コピー構築と一連のデフォルト構築とそれに続く割り当てとの間の同等性も必要です。
Jerry Coffin

怖い@JerryCoffin sheesh 使用しないことを約束します。
Hani Goc、2015

4
私は恐怖に基づいてそれを決定しません。禁止されている機能を使用する要素を保存する必要があるかどうかに基づいて決定します。
Jerry Coffin

3
@annoying_squid:追加する具体的で(信じている)正確な情報がある場合は、それを示す回答を追加してください。現在のところ、コメントには有用な情報が追加されていないようです。
Jerry Coffin 2017

39

C ++ 98の標準化中に、valarrayは、ある種の高速数学計算を可能にするように設計されました。ただし、その頃、Todd Veldhuizenは式テンプレートを発明し、blitz ++を作成しました。同様のテンプレートメタ技術が発明され、標準がリリースされる前にvalarrayがかなり時代遅れになりました。valarrayの最初の提案者であるIIRCは、標準化の途中でそれを放棄しましたが、これは(真の場合)助けにはなりませんでした。

ISTRが標準から削除されなかった主な理由は、誰も時間をかけて問題を徹底的に評価し、削除する提案を書いていないためです。

ただし、これらはすべて漠然と記憶された伝聞であることに注意してください。これを塩の粒と一緒に取り、誰かがこれを修正または確認することを望みます。


式テンプレートは、Vandevoordeにも同じように貢献できますよね?
Nikos Athanasiou 2014年

@ニコス:私が知っていることではありません。しかし、私は間違っている可能性があります。その読書を支持するものは何ですか?
sbi 2014年

1
それは「C ++テンプレート-完全なガイド」という本で言及されていますが、両方とも独立して発明しことは一般的に受け入れられていると思います。
Nikos Athanasiou 2014年

27

valarrayには構文糖があることを知っています

私はstd::valarrays構文糖に関してはそれほど多くはないと思います。構文は異なりますが、違いを「砂糖」とは呼びません。APIは奇妙です。C ++プログラミング言語std::valarrayのs のセクションでは、この異常なAPIと、sは高度に最適化されることが期待されているため、使用中に表示されるエラーメッセージはおそらく直感的ではないという事実に言及しています。std::valarray

好奇心から、約1年前に私はstd::valarray対戦しましたstd::vector。コードや正確な結果はもうありません(ただし、独自のコードを書くことは難しくありません)。GCC私が使用していた使用している場合、ほとんどのパフォーマンス上の利点を得るstd::valarray私の実装は、標準偏差を計算するための簡単な数学のためではなく(もちろん、及び、標準偏差はない複雑なこと、限り数学が行くように)。 大規模な各アイテムのstd::vector操作は、std::valarraysの操作よりもキャッシュの方が適していると思います。、からアドバイスを以下musiphil、私はからほぼ同じパフォーマンスを得ることができたvectorvalarray)。

結局、std::vectorメモリ割り当てや一時的なオブジェクト作成などに細心の注意を払いながら使うことにしました。


双方std::vectorstd::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::vectorstd::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。どちらも、要素に単一の連続したメモリブロックを割り当てます。
musiphil 2013年

1
@musiphil返答が長すぎてコメントできないため、返答を更新しました。
Max Lybbert 2013年

1
あなたのためにvalarray上記の例では、構築するために持っていなかったtemp valarrayオブジェクトを、あなただけ行っている可能性std::valarray<double> differences_from_mean = original_values - mean;、およびキャッシュの振る舞いは、のそれに類似していなければならないvector例。(ちなみに、meanそうintである場合double、そうでない場合は、必要になる可能性がありstatic_cast<double>(mean)ます。)
musiphil 2013年

をクリーンアップする提案をありがとうvalarray。それによってパフォーマンスが向上するかどうかを確認する必要があります。ためのようmeanであることint:それは間違いでした。私は最初にints を使用して例を記述しましたmeanが、その後、切り捨てが原因で実際​​の平均から非常に離れていることに気付きました。しかし、最初の編集で必要ないくつかの変更を見逃しました。
Max Lybbert 2013年

@musiphilその通りです。この変更により、サンプルコードはほぼ同じパフォーマンスになりました。
Max Lybbert 2013年

23

valarrayは、一部のFORTRANベクトル処理の良さをC ++でこすり落とすことになっています。どういうわけか、必要なコンパイラのサポートは実際には起こりませんでした。

Josuttisの本には、valarray(ここここ)に関する興味深い(いくぶん非難する)コメントが含まています

ただし、Intelは最近のコンパイラリリースでvalarrayを再検討しているようです(たとえば、スライド9を参照)。これは、4ウェイSIMD SSE命令セットが8ウェイAVXおよび16ウェイLarrabee命令によって結合されようとしていることを考えると、興味深い開発です。移植性の観点から、 (たとえば)組み込みよりもvalarray。


16

私は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()

2
私は、今日仕事でvalarrayについて知ったときにあなたがしたのとまったく同じ考えを文字通り持っていました。これからはc ++での数学処理の問題について、数学の観点からコードを理解するのがはるかに簡単に見えるため、valarrayを使用することになると思います。
Zachary Kraus

8

C ++ 11標準は次のように述べています。

valarray配列クラスは、特定の形式のエイリアシングがないように定義されているため、これらのクラスの操作を最適化できます。

C ++ 11 26.6.1-2を参照してください。


標準でどのフォームが定義されていると思いますか、それらを引用できますか?また、これらはコーディングトリックを使用して実装されていますか、それとも言語の他の場所でのエイリアシングルールに対するコンパイラベースの例外ですか?
underscore_d

2

std::valarrayあなたのような標準的な数学表記を使用することができますv1 = a*v2 + v3箱から出して。独自の演算子を定義しない限り、これはベクトルでは不可能です。


0

std :: valarrayは、数百万、場合によっては数千万のアイテムを含む配列があり、数百万のタイムステップを含むループでそれらを反復する、計算流体力学や計算構造力学などの重い数値タスクを対象としています。たぶん今日std :: vectorは同等のパフォーマンスを持っていますが、約15年前、効率的な数値ソルバーを記述したい場合、valarrayはほぼ必須でした。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.