uBLAS Overviewあらゆる種類の数学的なソフトウェアが 効率を失することなくC++ 言語で書かれ得るのなら、 それは素晴らしいことだ。しかし C++ 型システムを犯すことなくこれを達成する何かが発見されない限り、 Fortran やアセンブラ、アーキテクチャ特定の拡張に頼る方がいいだろう (Bjarne Stroustrup)。
この C++ ライブラリは行列とベクトルに関する基本的な線形代数の構築、 及びそれらに対応する抽象的な演算といった水準での科学技術計算を目指している。 基本的な設計目標は、以下の通りである:
もう一つの目的は、そのような行列とベクトルクラスを使うことによる抽象化の不利益が、 許容できるものかどうかを評価することである。
このライブラリの開発はいくつかの似たような努力により導かれた:
BLAS は最も広く使われている、基本的な線形代数構築のためのライブラリだろう。 そのため、これはデファクトスタンダードと呼ばれている。 そのインタフェースは手続き志向であり、個々の関数は単純な線形代数演算からはある程度抽象化されている。 Fortran とその最適化を使って実装されてきたという事実により、これはまた、 最も高速なライブラリのひとつだろう。我々はライブラリをオブジェクト指向で設計し実装することにしたので、 技術的なアプローチは異なる。しかし、我々のライブラリの言葉で BLAS の抽象化を表現することや、 これらの実装の効率を比較することは可能であるべきだろう。
Blitz++ は C++ で実装された印象的なライブラリである。その主な設計は、 多次元配列と、テンソルを含むそれに関連する演算子を志向している。 Blitz++ の作者は、ライブラリは式テンプレートとテンプレートメタプログラミングを使った実装上のテクニックによって、 同じような Fortran のコードと同等かそれ以上のパフォーマンスを達成した、と述べている。 しかし、我々が、我々自身の設計と実装のアプローチで開発するのには多少理由がある。 誰もが伝統的な線形代数や、他の数学的アルゴリズムを、 Blitz++ を使って実装しようとするかどうか、 我々は知らない。また、現在でも Blitz++ は実装のイディオムにより、最も進んだ C++ コンパイラを必要とするのである。一方 Blitz++ は、抽象化の不利益を許容可能な程度におさめるために、 式テンプレートの使用が不可欠である、ということを我々に解らせてくれた。
ROOMA の設計目標は多くの点で Blitz++ と似ているだろう。これは Blitz++ のクラスに関するコンセプトを、 偏微分方程式と理論物理学の領域から拡張した。実装は同じようなアーキテクチャをサポートしている。
MTL は C++ で基本的な線形代数演算をサポートする為のもう一つのアプローチである。 その設計は主に、 BLAS と C++ 標準テンプレートライブラリの影響を受けている。 線形代数ライブラリは BLAS と同等の機能性を提供しなければならない、というその洞察については、 我々も同じである。一方で我々は、 C++ 標準テンプレートライブラリのコンセプトは、 必要とされる数学計算をサポートするとは証明されていないと考えている。 もう一つの違いとして、 MTL は現在、式テンプレートを使っていないように見える。 これは 2 つの結果の内の 1 つをもたらす: つまり、表現力を失う可能性と、 パフォーマンスを失う可能性である。
数学的表記の用法は科学技術アルゴリズムの開発を容易にするだろう。 そこで、基本的な線形代数のコンセプトを実装している C++ ライブラリは、 選択された C++ 演算子を行列やベクトルクラスに対して注意深くオーバーロードするべきである。
我々は以下の通り原初的なものに対して演算子オーバーロードを利用することにした。
| 説明 | 演算子 |
|---|---|
| ベクトルと行列のインデックス | vector::operator(size_t i); |
| ベクトルと行列の代入 | vector::operator = (const vector_expression &); |
| ベクトルと行列に対する単項演算 | vector_expression operator - (const vector_expression
&); |
| ベクトルと行列に対する2項演算 | vector_expression operator + (const vector_expression
&, const vector_expression &); |
| ベクトルと行列の、スカラーとの乗算 | vector_expression operator * (const scalar_expression
&, const vector_expression &); |
次の通り、他の原初的なものに対しては演算子オーバーロードを使わないことにした。
| 説明 | 関数 |
|---|---|
| ベクトルが左項のベクトル・行列の乗算 | vector_expression prod<vector_type
> (const matrix_expression &, const vector_expression
&); |
| ベクトルが右項の行列・ベクトルの乗算 | vector_expression prod<vector_type
> (const vector_expression &, const matrix_expression
&); |
| 行列同士の乗算 | matrix_expression prod<matrix_type
> (const matrix_expression &, const matrix_expression
&); |
| ベクトルの内積 | scalar_expression inner_prod (const vector_expression
&, const vector_expression &); |
| ベクトル同士の乗算(返り値は行列) | matrix_expression outer_prod (const vector_expression
&, const vector_expression &); |
| 行列の転置 | matrix_expression trans (const matrix_expression
&); |
数値計算の効率という目的を達成するために、 C++ で抽象化を定式化する上でのふたつの困難を克服しなければならない。 つまり、一時オブジェクトと仮想関数呼び出しである。式テンプレートはこれらの問題を解決するが、 コンパイル時間を遅くする傾向がある。
ベクトルと行列に対する抽象的な式は通常、単項及び2項演算の組を構成する。 そのような式を評価する伝統的な方法は、まず合成関数の全ての構成要素を評価して一時オブジェクトにして、 続いて合成された結果を評価して、別の一時オブジェクトにするというものである。 この方法は、時間効率という点では小さな、空間効率という点では大きなベクトルと行列に対して、 高くつく。この問題を解決するアプローチは、最近の関数志向プログラミング言語から知られているように、 遅延評価を使うことである。 このアプローチの原理は、複雑な式の要素を賢く評価し、対象に直接代入するというものである。
二つの興味深く、危険な事実がある。
まず、ベクトルと行列に対する、要素の賢い評価を使うことの深刻な副作用を被るかもしれない。 行列とベクトルの積 x = A x を考えよう。 A1x の評価と、 x1 への代入は、右側を変化させるので、 A2x の評価は誤った結果となる。 この問題に対する我々の解決法は、代入の右辺を評価して一時オブジェクトにいれ、 それからこの一時オブジェクトを左辺に代入するというものである。 さらなる最適化を可能にするために、全ての代入演算子に対応するメンバ関数を提供している。 このメンバ関数を使うことで、プログラマは代入の左辺と右辺が独立であり、 要素の賢い評価と、対象への直接の代入が安全であることを確かなものとできる。
次に、計算量がひどく悪くなってしまう状況がある。 連鎖した行列とベクトルの積 A (B x) を考えてみよう。 A (B x) の従来の評価は二次式である。 B xi の遅延評価は線形である。 B xi の全ての要素は、その大きさに依存した線形時間を必要とするので、 連鎖した行列とベクトルの積 A (B x) の完全な遅延評価は三次式である。 そのような場合には、式の中に一時オブジェクトを再度取り入れる必要がある。
式の遅延評価は通常、項のクラス階層の定義となる。 これは結果として、ベクトルと行列の単一の要素にアクセスするために動的多相を利用することになるが、 動的多相は時間に関して高くつくことが知られている。 2,3年前、David Vandervoorde と Todd Veldhuizen はそれぞれ、一般的には式テンプレートと呼ばれる解決法を発見した。 式テンプレートは遅延評価を含み、動的多相を静的、つまりコンパイル時多相に置き換える。 式テンプレートは有名な Barton-Nackmann のトリックに強く依存していて、 '奇妙に定義された再帰的テンプレート'という言葉が Jim Coplien によって作られた。
式テンプレートは我々の実装の基礎となっている。
これもよく知られている事実だが、式テンプレートは現在使えるコンパイラに対する挑戦である。 Barton-Nackman のトリックを使うことで結果的に、必要な式テンプレートの量を著しく減らすことが出来た。
また、我々は従来の実装(つまり、式テンプレートを使わない)もサポートしている。
これは、開発サイクルをサポートするために、
ベクトルと行列の演算についての広範囲の領域限界と型チェックを備えている。
デバッグモードからリリースモードへの切り替えは、 <cassert> の
NDEBUG プリプロセッサシンボルによって行われている。
線形代数をサポートする全ての C++ ライブラリは、長期間存在してきた Fortran のパッケージ BLAS と比較されるだろう。ここでは、 BLAS の呼び出しが我々のクラスにどうマッピングされているかを説明する。
| BLAS Call | Mapped Library Expression | Mathematical Description | Comment |
|---|---|---|---|
_asum |
norm_1 (x) |
sum |xi| | ベクトルのノルムの合計を計算する。 |
_nrm2 |
norm_2 (x) |
sqrt (sum |xi|2 ) | ベクトルのユークリッドノルムを計算する。 |
i_amax |
norm_inf (x) |
max |xi| | ベクトルの最大ノルムを計算する。 BLAS はこの値を持つ最初の要素のインデックスを計算している。 |
_dot |
inner_prod (x, y)or |
xT y or xH y |
二つのベクトルの内積を計算する。 BLAS はいくつかのループの展開を実装している。 |
dsdot |
a + prec_inner_prod (x, y) |
a + xT y | double の精度で内積を計算する。 |
_copy |
x = y |
x <- y | ベクトルを別のベクトルにコピーする。 BLAS はいくつかのループの展開を実装している。 |
_swap |
swap (x, y) |
x <-> y | 二つのベクトルを入れ替える。 BLAS はいくつかのループの展開を実装している。 |
_scal |
x *= a |
x <- a x | ベクトルをスカラー倍する。 BLAS はいくつかのループの展開を実装している。 |
_axpy |
y += a * x |
y <- a x + y | スカラー倍されたベクトルを加える。 BLAS はいくつかのループの展開を実装している。 |
_rot |
t.assign (a * x + b * y), |
(x, y) <- (a x + b y, -b x + a y) | 平面回転を適用する。 |
_rotg |
(a, b) <- (? a / sqrt (a2 + b 2), ? b / sqrt (a2 + b 2)) or (1, 0) <- (0, 0) |
平面回転を構築する。 |
| BLAS Call | Mapped Library Expression | Mathematical Description | Comment |
|---|---|---|---|
_t_mv |
x = prod (A, x) or or |
x <- A x or x <- AT x or x <- AH x |
行列とベクトルの積を計算する。 |
_t_sv |
y = solve (A, x, tag) orinplace_solve (A, x, tag) ory = solve (trans (A), x, tag) orory = solve (herm (A), x, tag)or |
y <- A-1 x or x <- A-1 x or y <- AT-1 x or x <- AT-1 x or y <- AH-1 x or x <- AH-1 x |
三角行列の形式で連立一次方程式を解く。つまり、 A は三角行列である。 |
_g_mv |
y = a * prod (A, x) + b * y oror |
y <- a A x + b y or y <- a AT x + b y y <- a AH x + b y |
行列とベクトルの積のスカラー倍を加える。 |
_g_r |
A += a * outer_prod (x, y) or |
A <- a x yT + A or A <- a x yH + A |
階数 1 の更新を実行する。 |
_s_r |
A += a * outer_prod (x, x) or |
A <- a x xT + A
or A <- a x xH + A |
対称行列またはエルミート行列の階数 1 の更新を実行する。 |
_s_r2 |
A += a * outer_prod (x, y) +or |
A <- a x yT + a y
xT + A or A <- a x yH + a- y xH + A |
対称行列またはエルミート行列の階数 2 の更新を実行する。 |
| BLAS Call | Mapped Library Expression | Mathematical Description | Comment |
|---|---|---|---|
_t_mm |
B = a * prod (A, B) orB = a * prod (trans (A), B) orB = a * prod (A, trans (B)) orB = a * prod (trans (A), trans (B)) orB = a * prod (herm (A), B) orB = a * prod (A, herm (B)) orB = a * prod (herm (A), trans (B)) orB = a * prod (trans (A), herm (B)) orB = a * prod (herm (A), herm (B)) |
B <- a op (A) op (B) with op (X) = X or op (X) = XT or op (X) = XH |
二つの行列の積のスカラー倍を計算する。 |
_t_sm |
C = solve (A, B, tag) orinplace_solve (A, B, tag) orC = solve (trans (A), B, tag) oror or |
C <- A-1 B or B <- A-1 B or C <- AT-1 B or B <- A-1 B or C <- AH-1 B or B <- AH-1 B |
三角行列の係数を持つ連立一次方程式を解く。つまり、 A は三角行列である。 |
_g_mm |
C = a * prod (A, B) + b * C orC = a * prod (trans (A), B) + b * C orC = a * prod (A, trans (B)) + b * C orC = a * prod (trans (A), trans (B)) + b * C orC = a * prod (herm (A), B) + b * C orC = a * prod (A, herm (B)) + b * C orC = a * prod (herm (A), trans (B)) + b * C orC = a * prod (trans (A), herm (B)) + b * C orC = a * prod (herm (A), herm (B)) + b * C |
C <- a op (A) op (B) + b C with op (X) = X or op (X) = XT or op (X) = XH |
二つの行列の積のスカラー倍を加える。 |
_s_rk |
B = a * prod (A, trans (A)) + b * B orB = a * prod (trans (A), A) + b * B orB = a * prod (A, herm (A)) + b * B orB = a * prod (herm (A), A) + b * B |
B <- a A AT + b B
or B <- a AT A + b B or B <- a A AH + b B or B <- a AH A + b B |
対称行列またはエルミート行列の階数 k の更新を実行する。 |
_s_r2k |
C = a * prod (A, trans (B)) +orC = a * prod (trans (A), B) +orC = a * prod (A, herm (B)) +orC = a * prod (herm (A), B) + |
C <- a A BT + a B
AT + b C or C <- a AT B + a B T A + b C or C <- a A BH + a- B AH + b C or C <- a AH B + a- BH A + b C |
対称行列またはエルミート行列の階数 2 k の更新を実行する。 |
ライブラリは伝統的な密ベクトルと密行列、圧縮されたベクトルと行列、 基本的な疎ベクトルと疎行列といった記憶域レイアウトをサポートしている。 ベクトルと行列の最も一般的な構築の記述は以下の通りである。
| Construction | Comment | |
|---|---|---|
vector<T, |
密ベクトルの構築。記憶域は標準のベクタ(std::vector)によって提供される。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
vector<T, |
密ベクトルの構築。記憶域はヒープに作られる配列によって提供される。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
vector<T, |
Constructs a dense vector, storage is provided by a
stack-based array. The storage layout usually is BLAS compliant. 記憶域レイアウトは通常、 BLAS に対応している。 |
|
unit_vector<T> |
index 次の典型的な単位ベクトルの構築。
| |
zero_vector<T> |
ゼロベクトルの構築。 | |
sparse_vector<T, |
疎ベクトルの構築。記憶域は標準のマップ(std::map)によって提供される。 | |
sparse_vector<T, |
疎ベクトルの構築。記憶域はマップ配列によって提供される。 | |
compressed_vector<T> |
圧縮行列の構築。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
coordinate_vector<T> |
座標行列の構築。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
vector_range<V> |
密ベクトル、圧縮されたベクトル、疎ベクトルの部分ベクトルを構築する。 | |
vector_slice<V> |
密ベクトル、圧縮されたベクトル、疎ベクトルの部分ベクトルを構築する。 このクラスは通常、 BLAS の vector slices をエミュレートするために使われる。 |
|
matrix<T, |
密行列の構築。方向は行優先である。 記憶域は標準のベクタによって提供される。 | |
matrix<T, |
密行列の構築。方向は列優先である。 記憶域は標準のベクタによって提供される。 記憶域レイアウトは通常、 BLAS に対応している。 | |
matrix<T, |
密行列の構築。方向は行優先である。 記憶域はヒープに作られる配列によって提供される。 | |
matrix<T, |
密行列の構築。方向は列優先である。
記憶域はヒープに作られる配列によって提供される。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
matrix<T, |
密行列の構築。方向は行優先である。 記憶域はスタックに作られる配列によって提供される。 | |
matrix<T, |
密行列の構築。方向は列優先である。
記憶域はスタックに作られる配列によって提供される。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
identity_matrix<T> |
単位行列の構築。 | |
zero_matrix<T> |
ゼロ行列の構築。 | |
triangular_matrix<T, |
圧縮された三角行列の構築。方向は 行優先である。 | |
triangular_matrix<T, |
圧縮された三角行列の構築。方向は 列優先である。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
banded_matrix<T, |
圧縮された帯行列の構築。方向は 行優先である。 | |
banded_matrix<T, |
圧縮された帯行列の構築。方向は 列優先である。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
symmetric_matrix<T, |
圧縮された対称行列の構築。方向は 行優先である。 | |
symmetric_matrix<T, |
圧縮された対称行列の構築。方向は 列優先である。 記憶域レイアウトは通常、 BLAS に対応している。 | |
hermitian_matrix<T, |
圧縮されたエルミート行列の構築。方向は 行優先である。 | |
hermitian_matrix<T, |
圧縮されたエルミート行列の構築。方向は 列優先である。 記憶域レイアウトは通常、 BLAS に対応している。 | |
sparse_matrix<T, |
疎行列の構築。方向は 行優先である。 記憶域は標準のマップによって提供される。 | |
sparse_matrix<T, |
疎行列の構築。方向は 列優先である。 記憶域は標準のマップによって提供される。 | |
sparse_matrix<T, |
疎行列の構築。方向は 行優先である。 記憶域はマップ配列によって提供される。 | |
sparse_matrix<T, |
疎行列の構築。方向は 列優先である。 記憶域はマップ配列によって提供される。 | |
compressed_matrix<T, |
圧縮行列の構築。方向は 行優先である。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
compressed_matrix<T, |
圧縮行列の構築。方向は 列優先である。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
coordinate_matrix<T, |
座標行列の構築。方向は 行優先である。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
coordinate_matrix<T, |
座標行列の構築。方向は 列優先である。 記憶域レイアウトは通常、 BLAS に対応している。 |
|
matrix_row<M> |
密行列、圧縮された行列、疎行列の、i 行目を含む部分ベクトルの構築。 | |
matrix_column<M> |
密行列、圧縮された行列、疎行列の、j 列目を含む部分ベクトルの構築。 | |
matrix_range<M> |
密行列、圧縮された行列、疎行列の部分ベクトルの構築。 このクラスは通常、 BLAS のleading dimensions をエミュレートするために使われる。 | |
matrix_slice<M> |
密行列、圧縮された行列、疎行列の部分ベクトルの構築。 | |
triangular_adaptor<M, F> |
密行列、圧縮された行列、疎行列からの三角行列の構築。 このクラスは通常、 対応する BLAS の行列型を生成するために使われる。 | |
banded_adaptor<M> |
密行列、圧縮された行列、疎行列からの帯行列の構築。 このクラスは通常、 対応する BLAS の行列型を生成するために使われる。 | |
symmetric_adaptor<M> |
密行列、圧縮された行列、疎行列からの対称行列の構築。 このクラスは通常、 対応する BLAS の行列型を生成するために使われる。 | |
hermitian_adaptor<M> |
密行列、圧縮された行列、疎行列からのエルミート行列の構築。 このクラスは通常、 対応する BLAS の行列型を生成するために使われる。 |
互換性という理由で、ベクトルと行列に対して配列風のインデックス付けを提供している。 これはしかし、一時プロキシオブジェクトが必要なために、行列に対して高くつくこともある。
最も広く使われている C++ コンパイラをサポートするために、 我々の設計と実装は基本的に、テンプレートの部分特殊化には依存していない。
ライブラリは operator new と operator delete による、
標準準拠のメモリ割り当てを仮定している。
そのため、 MSVC 6.0 で実行するように意図されたプログラムはメモリの状態を検出するために、
_set_new_handler によって、 std::bad_alloc 例外を投げる正しい
new ハンドラを設定するべきである。
MSVC 6.0 で気が狂うほどの効率を得るためには、
ヘッダファイル config.hppの中で、BOOST_UBLAS_INLINE のプリプロセッサ定義を
__forceinline に変更すべきである。しかし我々は、この最適化が脆弱なものではないかと疑っている。
The following tables contain results of one of our benchmarks. This benchmark
compares a native C implementation ('C array') and some library based implementations.
The safe variants based on the library assume aliasing, the fast variants
do not use temporaries and are functionally equivalent to the native C implementation.
Besides the generic vector and matrix classes the benchmark utilizes special
classes c_vector and c_matrix, which are intended
to avoid every overhead through genericity.
The benchmark program was compiled with MSVC 6.0 and run on an Intel Pentium II with 333 Mhz. First we comment the results for double vectors and matrices of dimension 3 and 3 x 3, respectively.
| Operation | Implementation | Elapsed [s] | MFLOP/s | Comment |
|---|---|---|---|---|
| inner_prod | C array | 0.1 | 47.6837 | No significant abstraction penalty |
| c_vector | 0.06 | 79.4729 | ||
| vector<unbounded_array> | 0.11 | 43.3488 | ||
| vector<std::vector> | 0.11 | 43.3488 | ||
| vector + vector | C array | 0.05 | 114.441 | Abstraction penalty: factor 2 |
| c_vector safe | 0.22 | 26.0093 | ||
| c_vector fast | 0.11 | 52.0186 | ||
| vector<unbounded_array> safe | 1.05 | 5.44957 | ||
| vector<unbounded_array> fast | 0.16 | 35.7628 | ||
| vector<std::vector> safe | 1.16 | 4.9328 | ||
| vector<std::vector> fast | 0.16 | 35.7628 | ||
| outer_prod | C array | 0.06 | 85.8307 | Abstraction penalty: factor 2 |
| c_matrix, c_vector safe | 0.22 | 23.4084 | ||
| c_matrix, c_vector fast | 0.11 | 46.8167 | ||
| matrix<unbounded_array>, vector<unbounded_array> safe | 0.38 | 13.5522 | ||
| matrix<unbounded_array>, vector<unbounded_array> fast | 0.16 | 32.1865 | ||
| matrix<std::vector>, vector<std::vector> safe | 0.5 | 10.2997 | ||
| matrix<std::vector>, vector<std::vector> fast | 0.11 | 46.8167 | ||
| prod (matrix, vector) | C array | 0.06 | 71.5256 | No significant abstraction penalty |
| c_matrix, c_vector safe | 0.11 | 39.0139 | ||
| c_matrix, c_vector fast | 0.11 | 39.0139 | ||
| matrix<unbounded_array>, vector<unbounded_array> safe | 0.33 | 13.0046 | ||
| matrix<unbounded_array>, vector<unbounded_array> fast | 0.11 | 39.0139 | ||
| matrix<std::vector>, vector<std::vector> safe | 0.38 | 11.2935 | ||
| matrix<std::vector>, vector<std::vector> fast | 0.05 | 85.8307 | ||
| matrix + matrix | C array | 0.11 | 46.8167 | No significant abstraction penalty |
| c_matrix safe | 0.17 | 30.2932 | ||
| c_matrix fast | 0.11 | 46.8167 | ||
| matrix<unbounded_array> safe | 0.44 | 11.7042 | ||
| matrix<unbounded_array> fast | 0.16 | 32.1865 | ||
| matrix<std::vector> safe | 0.6 | 8.58307 | ||
| matrix<std::vector> fast | 0.17 | 30.2932 | ||
| prod (matrix, matrix) | C array | 0.11 | 39.0139 | No significant abstraction penalty |
| c_matrix safe | 0.11 | 39.0139 | ||
| c_matrix fast | 0.11 | 39.0139 | ||
| matrix<unbounded_array> safe | 0.22 | 19.507 | ||
| matrix<unbounded_array> fast | 0.11 | 39.0139 | ||
| matrix<std::vector> safe | 0.27 | 15.8946 | ||
| matrix<std::vector> fast | 0.11 | 39.0139 |
We notice a twofold performance loss for small vectors and matrices: first the general abstraction penalty for using classes, and then a small loss when using the generic vector and matrix classes. The difference w.r.t. alias assumptions is also significant.
Next we comment the results for double vectors and matrices of dimension 100 and 100 x 100, respectively.
| Operation | Implementation | Elapsed [s] | MFLOP/s | Comment |
|---|---|---|---|---|
| inner_prod | C array | 0.05 | 113.869 | No significant abstraction penalty |
| c_vector | 0.06 | 94.8906 | ||
| vector<unbounded_array> | 0.05 | 113.869 | ||
| vector<std::vector> | 0.06 | 94.8906 | ||
| vector + vector | C array | 0.05 | 114.441 | No significant abstraction penalty |
| c_vector safe | 0.11 | 52.0186 | ||
| c_vector fast | 0.11 | 52.0186 | ||
| vector<unbounded_array> safe | 0.11 | 52.0186 | ||
| vector<unbounded_array> fast | 0.06 | 95.3674 | ||
| vector<std::vector> safe | 0.17 | 33.6591 | ||
| vector<std::vector> fast | 0.11 | 52.0186 | ||
| outer_prod | C array | 0.05 | 114.441 | No significant abstraction penalty |
| c_matrix, c_vector safe | 0.28 | 20.4359 | ||
| c_matrix, c_vector fast | 0.11 | 52.0186 | ||
| matrix<unbounded_array>, vector<unbounded_array> safe | 0.27 | 21.1928 | ||
| matrix<unbounded_array>, vector<unbounded_array> fast | 0.06 | 95.3674 | ||
| matrix<std::vector>, vector<std::vector> safe | 0.28 | 20.4359 | ||
| matrix<std::vector>, vector<std::vector> fast | 0.11 | 52.0186 | ||
| prod (matrix, vector) | C array | 0.11 | 51.7585 | No significant abstraction penalty |
| c_matrix, c_vector safe | 0.11 | 51.7585 | ||
| c_matrix, c_vector fast | 0.05 | 113.869 | ||
| matrix<unbounded_array>, vector<unbounded_array> safe | 0.11 | 51.7585 | ||
| matrix<unbounded_array>, vector<unbounded_array> fast | 0.06 | 94.8906 | ||
| matrix<std::vector>, vector<std::vector> safe | 0.1 | 56.9344 | ||
| matrix<std::vector>, vector<std::vector> fast | 0.06 | 94.8906 | ||
| matrix + matrix | C array | 0.22 | 26.0093 | No significant abstraction penalty |
| c_matrix safe | 0.49 | 11.6776 | ||
| c_matrix fast | 0.22 | 26.0093 | ||
| matrix<unbounded_array> safe | 0.39 | 14.6719 | ||
| matrix<unbounded_array> fast | 0.22 | 26.0093 | ||
| matrix<std::vector> safe | 0.44 | 13.0046 | ||
| matrix<std::vector> fast | 0.27 | 21.1928 | ||
| prod (matrix, matrix) | C array | 0.06 | 94.8906 | No significant abstraction penalty |
| c_matrix safe | 0.06 | 94.8906 | ||
| c_matrix fast | 0.05 | 113.869 | ||
| matrix<unbounded_array> safe | 0.11 | 51.7585 | ||
| matrix<unbounded_array> fast | 0.17 | 33.4908 | ||
| matrix<std::vector> safe | 0.11 | 51.7585 | ||
| matrix<std::vector> fast | 0.16 | 35.584 |
For larger vectors and matrices the general abstraction penalty for using classes seems to decrease, the small loss when using generic vector and matrix classes seems to remain. The difference w.r.t. alias assumptions remains visible, too.
Copyright (©) 2000-2002 Joerg Walter, Mathias Koch
Permission to copy, use, modify, sell and distribute this document is granted
provided this copyright notice appears in all copies. This document is provided
``as is'' without express or implied warranty, and with no claim as to its
suitability for any purpose.
Last revised: 1/15/2003