行列の行優先レイアウトと列優先レイアウト


16

密行列計算のプログラミングで、列優先レイアウトより行優先レイアウトを選択する理由はありますか?

選択したマトリックスのレイアウトに応じて、速度を上げるためにキャッシュメモリを効果的に使用するために適切なコードを記述する必要があります。

行優先レイアウトは、より自然でシンプルに見えます(少なくとも私には)。しかし、Fortranで書かれたLAPACKのような主要なライブラリは列の主要なレイアウトを使用するため、この選択をした理由がいくつかあるに違いありません。


x列ベクトルでb = A * xを計算することを検討する場合、行優先Aに対して、ベクトルの内積A(i、:)^ T xを使用してb(i)を取得できます。列優先の場合、スカラー乗算ベクトルsum_i A(:、i)x(i)のみが必要な場合があります。私にとっては、コラム専攻の方がはるかに優れているようです!どう思いますか?
フイチャン

コラムが好きになるように自分を鍛えましょう。ベクトルを列として、またはそれらの転置を行として視覚化するのは簡単です。これにより、行列乗算の視覚化が非常に簡単になり、公開されている多くの数学を簡単に追跡できます。
マイクダンラベイ

回答:


18

列の主要なレイアウトは、Fortranで使用されるスキームであるため、LAPACKおよびその他のライブラリで使用されます。

一般に、メモリの帯域幅の使用率とキャッシュパフォーマンスの点で、メモリ内に配置されている順序で配列の要素にアクセスする方がはるかに効率的です。マトリックスの保存方法に応じて、これを活用するアルゴリズムを選択する必要があります。

内部記憶装置 列メジャー形式の内部ストレージ


11

既存のソフトウェアを考慮せずにバキュームでは、コードの観点から行メジャーより列メジャーを優先する理由はありません。ただし、ほとんどの数学文献は、ベクトルを行ではなく列として保存することにより、ベクトルを行列にグループ化する方法で記述されています。たとえば、あなたは完全な固有値方程式書くときXをAバツ=バツΛバツ行列には、列に書き込まれたすべての固有ベクトルが含まれます。あなたは本当にそれが他の方法で書かれていることを見たことはありません(統計の人々は行ベクトルが好きだと聞いていますが)したがって、ベクトルのセットであるマトリックスがある場合、任意の単一ベクトルのストレージが連続するように、最も初期のソフトウェアが列メジャー形式を想定するのは自然でした。したがって、私は伝統が今日に引き継がれたばかりで、昔のFortranとやり取りしたいなら、列メジャーを使いたいと思うと思います。したがって、ほとんどすべての非常に効率的な数値線形代数は、列メジャーで行われます。

Cが行メジャーである理由は、その配列構文の結果です。3行2列の配列をとして宣言します。double a[3][2]後のインデックスは前のインデックスよりも速く変化し、2D配列の場合は行が大きくなります。これを、左から右への自然な西洋の読み順と組み合わせると、行メジャーがより自然に見えるようになります。


2
これらは貧弱な議論だと思います。'' 'double a [3] [2]' ''の最後のインデックスが最も速く変化するという事実は偶然ではありません。 '' 'real(3,2)' ''配列がある場合は、逆の方法で行います。
ヴォルフガングバンガース

1
さらに、ほとんどすべての非常に効率的な数値線形代数が列優先であることは事実ではありません。これは、BLASとLAPACKについては依然として当てはまりますが、過去15年間に登場したすべての主要な線形代数ライブラリについては、まったく当てはまりません。たとえば、PETScとTrilinosはどちらも行メジャースパースマトリックスストレージ形式を使用します。
ウルフギャングバンガース

私は、Cの慣習が意識的な決定であり、おそらく自然な読み順に基づいていることを知っています。多分数値線形代数を念頭に置いて設計されたのではないことを意味し、それが行優先であることを偶然にしました。第二に、私は引数がスパース行列を保持することを意図していませんでした。スパースの場合、圧縮された行形式と列形式の両方を備えた、多少の混在があります。
ビクター

5
要点を説明するためではありませんが、Cはもともと浮動小数点数を持たないPDP-11のようなシステム上で実行される、以前の言語BとBCPLに基づいたシステム言語でした。彼らが数値を念頭に置いて設計したと言うのは、かなりストレッチです。
ビクター

7
Cの行列が最後のインデックスを最も速く移動する理由は、Cに行列がないためです。ベクトルのベクトルを持ち、メモリのソリッドブロックとして、または配列へのポインタの配列として透過的に実装できます。Fortranと互換性のあるインデックス順を作成することは、デニスリッチーのレーダーでもありませんでした(推測しています)。
マイクダンラベイ

2

列優先の順序はより自然なようです。たとえば、ピクチャごとにムービーをファイルに保存する場合、列順を使用するとします。これは非常に直感的で、行優先の順序で保存する人はいません。

C / C ++のプログラマーである場合は、列(Eigen、Armadilloなど)のデフォルトの列優先順序で、より高いレベルのライブラリを使用する必要があります。C / C ++は行列のインデックス付けを思い出させるものを提供していますが、マニアックな人だけが行優先の生のCポインタを使用します。

単純化するために、行優先の順序を持​​つすべてのものは、少なくとも奇妙なものとして形成されると見なされるべきです。スライスごとのスライスは単純な自然順序であり、列優先順序(Fortranのような)を意味します。私たちの父親/母親は、彼らがそれを選んだ非常に良い理由がありました。

残念ながら、明らかになる前に、おそらく経験不足のため、いくつかの興味深いライブラリが行優先の順序で作成されました。

メモリを介して1ステップで右インデックスがより速く変化する行優先順序の定義を思い出してください。たとえば、A(x、y、z)はz-indexです。これは、メモリ内で異なるスライスのピクセルが隣接していることを意味します。したくない 映画A(x、y、t)の場合、最後のインデックスは時間tです。行優先モードでムービーを保存することは単に不可能であることを想像するのは難しくありません。


2

m×n

  • mj×m+j
  • mjj×n+

次のアルゴリズムを想像してください。

for i from 1 to m
   for j from 1 to n
      do something with m(i,j)

×m+j

結論:

  1. はい、重要ですが、選択はデータのアクセス方法によって異なります。前の例で、column-orderが使用されている場合、できることは2つのループを交換することです。

  2. 経験則:急速に変化するインデックスは、メモリ内の連続した場所にマッピングする必要があります。

  3. さらに重要なことは、選択の影響を測定/ベンチマークすることは、多くのパラメーター(データのサイズ、キャッシュのサイズ、使用言語が複数のインデックスを線形インデックスにマッピングする方法、操作の方法システムが仮想メモリを管理し、使用する線形代数ライブラリにループがネストされる方法...)

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