145


42

私はベクトルがとても好きです。 彼らは気の利いたと速いです。 しかし、私はvalarrayと呼ばれるものが存在することを知っています。 ベクトルの代わりにvalarrayを使うのはなぜですか? 私は、valarrayに構文上の糖があることを知っていますが、それ以外にいつ有用なのでしょうか。

7 Answer


134


valarrayは、間違った時に間違った場所で生まれた一種の孤児です。 これは最適化の試みです。特に、書かれたときに重い数学で使われていたマシン、特にCraysのようなベクトルプロセッサのためのものです。

ベクトルプロセッサの場合、一般的にやりたいことは、配列全体に単一の操作を適用し、次に配列全体に次の操作を適用することなどです。

ただし、かなり小さい配列を扱っているのでなければ、キャッシュではうまく機能しません。 最近のほとんどのマシンでは、(可能な限り)一般的に望んでいるのは、配列の一部をロードし、それに対する操作をすべて実行してから、配列の次の部分に進むことです。

valarrayはエイリアシングの可能性を排除することも想定されています。これは(少なくとも理論的には)コンパイラが値をレジスタに自由に格納できるため速度を向上させることができます。 しかし、実際には、実際の実装でこれを十分に活用できるかどうかはまったくわかりません。 コンパイラーのサポートなしには普及していないし、それが普及していない限り、誰かがそれをサポートするためにコンパイラーで作業するという問題に遭遇する人はいないでしょう。

また、valarrayで使用するための(文字通り)厄介な補助クラスの配列もあります。 slice、slice_array、gslice、およびgslice_arrayをvalarrayの一部で再生し、それを多次元配列のように動作させることができます。 また、mask_arrayを使って操作を「隠す」こともできます(例: xの項目をyに追加しますが、zがゼロ以外の位置にある場合のみです。 単純にvalarrayを使用する以上のことを行うには、これらの補助的なクラスについて多くを学ぶ必要があります。そのうちのいくつかはかなり複雑で、どれも(少なくとも私には)非常によく文書化されていません。

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

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

ですので、Cプログラマが `valarray`を大量に使用し始めるとは完全には確信していませんが、スピードが改善される状況は少なくともいくつかあります。


64


Valarrays(値配列)は、Fortranの速度の一部をCにすることを目的としています。 コンパイラがコードについて仮定して最適化できるように、ポインタの集まりを作成しないでください。 (Fortranが非常に高速である主な理由は、ポインター型がないため、ポインターの別名割り当てができないためです。)

標準のその部分はもう少し作業を使用することができますが、Valarraysはあなたが合理的に簡単な方法でそれらをスライスすることを可能にするクラスも持っています。 それらをサイズ変更することは破壊的であり、それらはイテレータを欠いています。

それで、それがあなたが働いている数であり、便利さがそれほど重要ではないならば、valarraysを使ってください。 そうでなければ、ベクトルはもっとずっと便利です。


36


C 98の標準化中、valarrayはある種の高速数学計算を可能にするように設計されました。 しかし、その頃にはTodd Veldhuizenが式テンプレートを発明してhttp://www.oonumerics.org/blitz / [blitz]を作成し、同様のテンプレートメタ技術が発明されたため、標準がリリースされるまではvalarrayはほとんど時代遅れになっていました。 valarrayの最初の提案者であるIIRCは標準化の途中でそれを放棄しました。

それが規格から削除されなかった主な理由は誰もが問題を徹底的に評価し、それを削除するための提案を書くのに時間をかけなかったということです。

_しかし、これはすべて漠然と聞いたことを覚えていることに注意してください。


26


_ valarraysには構文糖質があることは知っています _

私は、 `std

valarrays`が統語的な糖という意味ではあまり意味がないと私は言わなければなりません。 構文は異なりますが、違いを「砂糖」とは言いません。 APIは変です。 Cプログラミング言語の std :: valarray`の節では、この珍しいAPIと、 std :: valarray`は非常に最適化されていると期待されているので、それらを使用している間に得られるエラーメッセージはおそらく直感的ではありません。

好奇心から、1年ほど前に私は `std

vector`に対して` std :: valarray`を追加しました。 私はもうコードも正確な結果も持っていません(あなた自身で書くのは難しくないはずですが)。 GCCを使う私は_did_を使って単純な数学で `std :: valarray`を使うと少しパフォーマンスが向上しますが、標準偏差を計算するために私の実装ではそうではありません。 [line-through]*I suspect that operations on each item in a large

`std

vector`は、` std :: valarray`s。 NOTE 、https://stackoverflow.com/users/424632/musiphil [musiphil]からのアドバイスに従って、 「vector」と「valarray」でほぼ同じパフォーマンスを得ることができました)。

*結局、メモリの割り当てや一時的なオブジェクトの作成などに細心の注意を払いながら、 `std

vector`を使うことにしました。

'' '' '

`std

vector`と` std :: valarray`は両方とも連続したブロックにデータを格納します。 しかし、それらは異なるパターンを使用してそのデータにアクセスします。さらに重要なことに、 std :: valarray`のAPIは std :: vector`のAPIとは異なるアクセスパターンを推奨します。

標準偏差の例では、特定のステップでコレクションの平均と各要素の値と平均の差を見つける必要がありました。

`std

valarray`では、私は以下のようにしました:

std::valarray original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray temp(mean, original_values.size());
std::valarray differences_from_mean = original_values - temp;
私は `std

slice`や` std :: gslice`を使ったほうが賢いかもしれません。 もう5年以上経ちました。

`std

vector`では、次の行に沿って何かをしました。

std::vector original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();

std::vector 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(), mean));

今日、私は確かにそれを違うように書くでしょう。 他に何もしなければ、私はC 11ラムダを利用するでしょう。

これら2つのコードの断片が異なる動作をすることは明らかです。 例えば、 `std

vector`の例は` std :: valarray`の例のように中間のコレクションを作成しません。 しかし、違いは std :: vector`と std :: valarray`の違いに関係しているので、それらを比較するのは公平だと思います。

私がこの答えを書いたとき、2つの `std

valarray`(` std :: valarray`の例の最後の行)から要素の値を引くことは `stdの対応する行よりもキャッシュにやさしくないだろうと私は疑っていました:: vector`の例(これは最後の行でもあります)

しかし、それは判明

std::valarray original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray differences_from_mean = original_values - mean;
  • `std :: vector`の例と同じことをし、ほぼ同じ性能を持ちます。 結局、問題はあなたがどのAPIを好むかということです。


23


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

Josuttisの書籍には、valarray(http://books.google.co.uk/books?id=n9VEG2Gp5pkC)に関する興味深い(やや不愉快な)解説が含まれています。

しかし、Intelは最近のコンパイラリリースでvalarrayを再検討しているようです(例: スライド9を参照)。彼らの4-way SIMD SSE命令セットが8-way AVXと16-way Larrabee命令によって結合されようとしていることを考えれば、これは興味深い展開です。そして移植性のためには本質的な(言う)よりもvalarray。


11


私はvalarrayの良い使い方を見つけました。 派手な配列のようにvalarrayを使うのです。

auto x = linspace(0、2 * 3.14、100); (x、sin(x)sin(3.f * x)/ 3.f sin(5.f * x)/ 5.f)をプロットします。
https://i.stack.imgur.com/Q799S.png[image:https://i.stack.imgur.com/Q799S.png[画像の説明をここに]]]

上記のvalarrayを使って実装することができます。

valarray linspace(フロート開始、フロート停止、intサイズ){valarray v(サイズ); for(int i = 0; i arange(float start、float step、float stop){int size =(ストップ - スタート)/ step; valarray v(size); for(int i = 0; i

また、Pythonスクリプトが必要です。

sys、posix_ipc、os、struct import matplotlib.pyplotをpltとしてインポートする

sz = int(sys.argv [1])f = posix_ipc.SharedMemory( "plot1")x = [0] * sz y = [0] *範囲内のi(sz):x [i]、y [ i] = struct.unpack( 'ff'、os.read(f.fd、8))os.close(f.fd)plt.plot(x、y)plt.show()


7


C 11規格はこう言います:

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

C 11 26.6.1-2を参照のこと。