1. ホーム
  2. python

[解決済み] 連続配列と非連続配列の違いは何ですか?

2022-06-01 19:14:32

質問

この質問では numpyマニュアル には、reshape()関数について、次のように書かれています。

>>> a = np.zeros((10, 2))
# A transpose make the array non-contiguous
>>> b = a.T
# Taking a view makes it possible to modify the shape without modifying the
# initial object.
>>> c = b.view()
>>> c.shape = (20)
AttributeError: incompatible shape for a non-contiguous array

私の質問です。

  1. 連続配列と非連続配列とは何ですか?C言語の連続したメモリブロックのようなものでしょうか? 連続したメモリブロックとは何ですか?
  2. この 2 つの間にパフォーマンスの違いはありますか。どのような場合にどちらかを使うべきでしょうか?
  3. なぜ転置は配列を非連続にするのですか?
  4. なぜ c.shape = (20) はエラーを投げます incompatible shape for a non-contiguous array ?

ご回答ありがとうございました。

どのように解決するのですか?

連続配列は、切れ目のないメモリブロックに格納された配列にすぎません。配列の次の値にアクセスするには、次のメモリアドレスに移動するだけです。

2次元配列 arr = np.arange(12).reshape(3,4) . このようになります。

コンピュータのメモリ内には arr はこのように格納されます。

これはつまり arr Cの連続した 配列であるため は連続したメモリブロックとして格納されるからです。次のメモリアドレスは、その行の次の行の値を保持しています。列を下に移動したい場合は、3 つのブロックを飛び越えるだけです (たとえば、0 から 4 に移動するには、1、2、3 を飛び越えることになります)。

での配列の転置は arr.T で転置すると、隣接する行のエントリが隣接するメモリアドレスに存在しなくなるため、C の連続性が失われることを意味します。しかし arr.T Fortranの連続した であるため はメモリの連続したブロックにあるためです。


パフォーマンス面では、互いに隣接するメモリ アドレスへのアクセスは、より広がっているアドレスへのアクセスよりも高速であることが非常に多いです (RAM から値をフェッチするには、多数の隣接するアドレスをフェッチして CPU にキャッシュする必要があります)。これは、連続した配列に対する操作がより速くなることが多いことを意味します。

C の連続したメモリ レイアウトの結果として、行単位の操作は列単位の操作よりも通常高速になります。例えば、通常

np.sum(arr, axis=1) # sum the rows

の方が若干速いです。

np.sum(arr, axis=0) # sum the columns

同様に、Fortranの連続配列の場合、列に対する操作は若干速くなります。


最後に、なぜFortran連続配列は新しい形状を割り当てることによって平坦化できないのでしょうか?

>>> arr2 = arr.T
>>> arr2.shape = 12
AttributeError: incompatible shape for a non-contiguous array

これを可能にするために、NumPy は、行を arr.T をこのようにまとめる必要があります。

(設定することで shape 属性を直接指定すると、C言語の順序を仮定します - つまり、NumPyは行単位で操作を実行しようとします)。

これは不可能です。どんな軸でも、NumPyには 定数 のストライド長(移動するバイト数)が必要です。平坦化 arr.T をこのように平らにすると、配列の連続した値を取得するために、メモリ内で前方や後方にスキップする必要があります。

もし私たちが arr2.reshape(12) と書くと、NumPy は arr2 の値を新しいメモリブロックにコピーします(このシェイプの元のデータに対するビューを返すことができないため)。