Numpy: NumPyに整列されたアロケーターを使用しますか?

作成日 2014年11月26日  ·  68コメント  ·  ソース: numpy/numpy

32ビットWindowsで障害が発生したNumPy1.9でのf2py回帰に関して、問題は、NumPyが保証されたアライメントを提供するアロケーターの使用を開始する必要があるかどうかです。

https://github.com/scipy/scipy/issues/4168

01 - Enhancement

最も参考になるコメント

この機能は私にとって非常に役立ちます。 DMAコントローラーで64バイトの整列データを使用する必要があるFPGAデバイス(Altera A10GX)を使用しています。これにより、コードが40倍高速化されます(!!!)。 @nachiketにも私と同じ問題があるのではないかと@eamartinが使用しているものに似たものを書きましたが、これはちょっとしたハックです。

全てのコメント68件

これは、すべてのプラットフォームで機能するアロケーターの一例です。 それは恥知らずにこれに基づいています:

https://sites.google.com/site/ruslancray/lab/bookshelf/interview/ci/low-level/write-an-aligned-malloc-free-function

これを行う方法は多くなく、同様のコードがネット上に浮かんでいるため、この方法で拡張しても問題ない可能性があります。 (それに加えて、reallocを実装していません。)

このコードをnumpy/core/include/numpy/ndarraytypes.hドロップすると、新しく割り当てられたndarrayがすべてのプラットフォームで適切に配置されるようになります。

このプラットフォームに依存しないコードは、多分に置き換えることができますposix_memalign() POSIX上および_aligned_malloc() Windows上で。 ただし、 posix_memalign()realloc()を組み合わせることはできないため、自分で実装する方がおそらく良いでしょう。

#define NPY_MEMALIGN 32   /* 16 for SSE2, 32 for AVX, 64 for Xeon Phi */ 

static NPY_INLINE
void *PyArray_realloc(void *p, size_t n)
{
    void *p1, **p2, *base;
    size_t old_offs, offs = NPY_MEMALIGN - 1 + sizeof(void*);    
    if (NPY_UNLIKELY(p != NULL)) {
        base = *(((void**)p)-1);
        if (NPY_UNLIKELY((p1 = PyMem_Realloc(base,n+offs)) == NULL)) return NULL;
        if (NPY_LIKELY(p1 == base)) return p;
        p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1));
        old_offs = (size_t)((Py_uintptr_t)p - (Py_uintptr_t)base);
        memmove(p2,(char*)p1+old_offs,n);    
    } else {
        if (NPY_UNLIKELY((p1 = PyMem_Malloc(n + offs)) == NULL)) return NULL;
        p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1));   
    }
    *(p2-1) = p1;
    return (void*)p2;
}    

static NPY_INLINE
void *PyArray_malloc(size_t n)
{
    return PyArray_realloc(NULL, n);
}

static NPY_INLINE
void *PyArray_calloc(size_t n, size_t s)
{
    void *p;
    if (NPY_UNLIKELY((p = PyArray_realloc(NULL,n*s)) == NULL)) return NULL;
    memset(p, 0, n*s);
    return p;
}

static NPY_INLINE        
void PyArray_free(void *p)
{
    void *base = *(((void**)p)-1);
    PyMem_Free(base);
} 

整列されたアロケータを追加するブランチがすでにあります。掘り下げます。

このようなものを使用することで、pythons tracemallocフレームワークとスパースメモリを使用するオプションを破棄します(aligned_callocはありません)。
@njsmith 3.5がリリースされる前に、Python連携して、スロットにさらに別のアロケーターを追加してもよろしいですか? 彼らはすでに私たちだけのためにcallocを追加しました、私たちが今それを使うことができなければ、それは恥です。

おそらく、 PyMemAllocatorExのコンテキストデータで整列を渡すことができますか? ただし、NumPyは2.6以降のPythonバージョンをサポートする必要があるため、Python3.5でこれを実行しても問題が解決しない場合があります。

3.5より前にこれについてPython開発者と関わることは良い考えだと思います。
しかし、私はまだ私たちが整列を使用する正当な理由があると確信していません
短期的にはアロケーター。 struct {
double、double}は、実際にはwin32またはでmallocよりも優れた配置が必要です。
SPARC、それが本当なら何も機能しないからです。
2014年11月26日09:10、「JulianTaylor」 [email protected]は次のように書いています。

整列されたアロケータを追加するブランチがすでにあります。掘り下げます。

このようなものを使用することで、pythonを使用するオプションを破棄します
tracemallocフレームワークとスパースメモリ(aligned_callocはありません)。
@njsmithhttps ://github.com/njsmithあなたは喜んで関与しますか
python devsは、3.5がリリースされる前に、スロットにさらに別のアロケーターを追加します。
リリース? 彼らはすでに私たちのためだけにcallocを追加しました、もし私たちが
今は使えません。


このメールに直接返信するか、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312#issuecomment-64534021

f2pyに関する質問は、Cの最小要件ではなく、Fortranが必要とするアライメントでした。速度も問題です。 データが適切に配置されている場合、インデックス作成とSIMDの両方がより適切に機能します。

整列されたアロケーターを使用する理由は、確かに速度とSSE / AVXの確保にある可能性があります
互換性は、異なるものを取ることから来る数値ジッターを取り除きます
異なる位置に配置されたデータのコードパス。

f2pyは、FortranのISOCバインディング標準よりも古いものです。
Worksは、基本的に、Fortranとのインターフェースに関する事実上の標準的な方法です。
Cで、誰もが広く使用しています。 この経験に照らして、それは
システムmallocによって提供されるアライメントで十分であることを明確にします。
私たちにとって実際に重要なFortranコンパイラー。

IntelによるAVXには32バイトのアライメントが推奨されていることに注意してください: https

@pitrou XeonPhiには64バイトのアライメントをお勧めします。 私のコード例のNPY_MEMALIGNの定義の背後にあるコメントを見てください。

調整された割り当てを提供する際の主な問題は、ATMができることです。
tracemallocインフラストラクチャにフックするか、調整された割り当てを行います。
これを修正するには、CPythonアップストリームとの調整が必要になります(を参照)

4663)。

2014年12月5日16:44、「SturlaMolden」 [email protected]は次のように書いています。

@pitrouhttps ://github.com/pitrouそしてXeonには64ビットが推奨されます
ファイ。 のNPY_MEMALIGNの定義の背後にあるコメントを見てください
私のコード例。


このメールに直接返信するか、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312#issuecomment-65816753

したがって、CPythonの問題はhttp://bugs.python.org/issue18835にあります。

realloc()の複雑さを考えると、CPythonが3.5の時間枠でこれを解決することを期待するのは現実的ではないかもしれません。 Numpyは、代わりに独自の整列された割り当て済みラッパーを使用する必要があります(PyMem APIに準拠し、とにかくtracemallocを利用できるはずです)。

このようなアロケータのコードは上記に含まれています。 @juliantaylorの議論は理解できませんが、彼はおそらく私よりもこれをよく理解しています。

しかし、彼がcallocについて何を意味していたかは理解できます。 callocは単なるmallocおよびゼロへのmemsetではあり

実際、CPython 3.5にはPyMem_Callocとその仲間がいます。
@juliantaylorは、OS関数(posix_memalignなど)を使用する実装ケースを検討していたと思います。 しかし、それは必要ではないようです。

ちなみに、 @ sturlamoldenの場合、スニペットはPyArray_Mallocとその仲間を再定義しますが、配列の割り当てではPyDataMem_NEWを使用しているようです。 私は何かを誤解していますか?

もう1つの考えは、整列された割り当ては小さなアレイでは無駄になる可能性があるということです。 おそらく、それを下回ると標準割り当てが使用されるしきい値が必要ですか?
また、配置を構成可能にする必要がありますか?

アロケータは、NumPy1.9ではPyArray_mallocおよびPyArray_freeと呼ばれます。 NumPy1.10では多くの変更があります。

本気ですか? PyArray_NewFromDescr_int()はnpy_alloc_cache()を呼び出し、npy_alloc_cache()はPyDataMem_NEW()を呼び出します。

Numpyには複数の割り当てインターフェースがあり、それらにはあまり明白ではありません
名前。 PyArray_malloc / freeは、「通常の」割り当てに使用されます(例:オブジェクト
構造体)。 データバッファ(ndarray->データポインタ、内部の一時バッファ
ただし、ufuncsなど)はPyDataMem_NEWを介して割り当てられます。

7:48 PMの木、2015年1月15日には、Sturla Molden [email protected]
書きました:

Numpy 1.9では、アロケータはPyArray_mallocおよびPyArray_freeと呼ばれています。


このメールに直接返信するか、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312#issuecomment-70149126

ナサニエル・J・スミス
ポスドク研究員-情報学-エディンバラ大学
http://vorpus.org

PyDataMem_NEWがNumPy1.9でmallocを呼び出しているようです。
https://github.com/numpy/numpy/blob/3975e095013119cfdbb9405ca95e6c723eb862d3/numpy/core/src/multiarray/alloc.c

@njsmithええ、

パッチを適用してPR#5457を作成しました。 アプローチに関するフィードバックがあればいいのですが。

私の知る限り、現在、整列したものを使用するメリットはありません。
numpyのアロケーター?

金には、2015年1月16日19:15で、アントワーヌPitrou [email protected]
書きました:

PR#5457https ://github.com/numpy/numpy/pull/5457を作成しました
パッチ。 アプローチに関するフィードバックがあればいいのですが。


このメールに直接返信するか、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312#issuecomment-70305997

ナサニエル・J・スミス
ポスドク研究員-情報学-エディンバラ大学
http://vorpus.org

Numbaを使用して、AVXベクトル命令が最適なパフォーマンスを得るために32バイトのアライメントが必要であると判断しました。 AVXを有効にしてNumpyをコンパイルする場合(特定のコンパイラオプションが必要だと思います)、配置によっても違いが生じるはずです。

好奇心から、実際の測定値はありますか? そこでb / cに聞いてみます
これらのことに影響を与える非常に多くの要因があります(異なるオーバーヘッド/速度
さまざまなアレイサイズでのトレードオフ、メモリアロケータの詳細-これ
また、配列サイズが異なれば動作も異なります-など)難しいと思います
最終的に0.5%のエンドツーエンドのスピードアップか50%のスピードアップかを推測します
エンドツーエンドのスピードアップか何か。

金には、2015年1月16日20:16で、アントワーヌPitrou [email protected]
書きました:

Numbaを使用して、AVXベクトル命令には32バイトが必要であると判断しました。
最適なパフォーマンスのための調整。 AVXを有効にしてNumpyをコンパイルする場合
(特定のコンパイラオプションが必要だと思います)、アライメントは
違いも。


このメールに直接返信するか、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312#issuecomment-70315434

ナサニエル・J・スミス
ポスドク研究員-情報学-エディンバラ大学
http://vorpus.org

i5-4210uのfwiw単純なロードアドストアテストで16バイトと32バイトの整列データに有意差は見られません。最小サイクル数は5%少ないようですが、中央値と10パーセンタイルは1%と同じです。

それはAVXでですか?

@seibertは、Numpy配列を使用してNumbaでいくつかのAVX測定(つまり、LLVMを使用したジャストインタイムコード生成)を行いました。正確な数値を取得するために、もう一度実行してみると思います:-)

最新のNumbaマスターのベンチマークは次のとおりです。

http://nbviewer.ipython.org/gist/seibert/6957baddc067140e55fe

a + b * fabs(a)操作を実行するfloat32アレイ(サイズ= 10000)の場合、Intel Core i7-4820K CPU @ 3.70GHz(Ivy Bridge)で実行すると40%の違いが見られます。

(LLVM 3.5オートベクトライザーがアライメントの問題を修正するためのループピーリングコードを生成していないことに少し戸惑っています。gccまたはclangがこれを修正しても驚かないでしょう。)

ターゲットにする予定の他のアーキテクチャ(HSA標準をサポートするAMD APUなど)の場合、アライメントのニーズはより厳しく、OpenCL開発者は、最高のパフォーマンスを得るためにアレイに256バイトのアライメントがあることを提案しています。

40%が過度のペナルティのように見えるかどうかはわかりませんが、i5 Haswellでゼロが表示されます(これにより、avxのパフォーマンスが大幅に低下します)
jitコンパイラが2つの異なるバージョンのループを作成している可能性がありますか?
これのアセンブリレベルのプロファイルがありますか(たとえば、perfレコードを介して)?

また、ターボブーストが2番目のループで開始され、それを無効にしたり、適切なpmuを監視したりすることも興味深いかもしれません。

このシステムをベンチマークに使用しているため、BIOSでTurboBoostを無効にしました。

JITは1回だけ実行され(Numbaが特定の入力タイプのセットに対して関数をコンパイルすると、それをキャッシュします)、リンクされたノートブックのセル6によるベンチマークの前にJITがトリガーされます。

これまでperfrecordを使用したことはありませんが、調べてみます。 ベンチマークをどのように実行しましたか? (実際には、ミスアライメントが利用可能なL2キャッシュ帯域幅のある種の非効率的な使用を引き起こしている場合、HaswellはIvy Bridgeよりも優れていると思います。)

Intelのこのホワイトペーパーでは、6ページの下部に配置の問題に関するもう少し情報が記載されています。

https://software.intel.com/sites/default/files/m/d/4/1/d/8/Practical_Optimization_with_AVX.pdf

アラインされていない32バイトベクトルでIntelAVX命令を使用すると、キャッシュラインが64バイトであるため、1秒ごとにキャッシュラインが分割されます。 これにより、16バイトのベクトルを使用するIntel SSEコードと比較して、キャッシュラインの分割レートが2倍になります。 メモリを大量に消費するコードでキャッシュラインの分割率が高いと、パフォーマンスが低下する可能性が非常に高くなります。 そのため、Intel AVXで使用するには、データを32バイトに揃えることを強くお勧めします。

HaswellのL2キャッシュ帯域幅はSandy / Ivy Bridgeの2倍であるため、配列のずれによる影響がHaswellに大きく影響しない可能性があります...

私の簡単なベンチマークはこれです:
https://gist.github.com/juliantaylor/68e578d140f427ed80bb
そのi7で見るのは面白いでしょう

Core i5-2500K(Sandy Bridge)での結果:
4644 6656 7704 10100

ベンチマークの開始時に、より長いウォームアップフェーズを追加する必要があることに注意してください。

    for (i=0; i<10; i++)
        add(a, 1);

それは平均/中央値/ ...ですか? これは非常に重要に見えますが、IntelはHaswellのために多くの作業を行ったようです。

これは、ループで数百回実行した後のベンチマークからのほぼ安定した出力です。

パックされたSSE2実装を追加しました。図(i5-2500K)は次のとおりです。
4660 6492 7468 5108 10096
(32B整列AVX、16B(非)整列AVX、8B(非)整列AVX、整列SSE2、スカラー)

更新されたソースは次のとおりです: https

Core i5-4200U(ラップトップHaswell CPU)からのほぼ安定した出力:
4120 4152 4148 4260 7308

少なくともここでは、ミスアライメントされたAVXは、同様のアライメントを持つSSE2よりも悪くはありません。

これが私のMBP(クアッドコアi7-3635QM、2.4 GHz、Ivy Bridge)からのものです:
5060 4932 5820 5704 5040

gcc 4.8.1がコードのコンパイルを拒否したため(clangも)、 avxintrin.himmintrin.h 、Inteliccでコンパイルする必要がありました。

@ sturlamolden 、iccがスカラーループ(数値の最後の結果)をベクトル化している可能性はありますか?

(追加に2つの別々の入力配列が含まれる場合、ベンチマークはおそらくキャッシュサブシステムにより多くの圧力をかけることに注意してください)

何も思いつきません。

@sturlamoldenこのコードは、一部のSSE2とのインターフェースに非常に役立ちました。

私はWindowsで1つの問題を抱えていました(2015を含むVCのすべてのバージョン)この行が好きではありません

memmove((void*)p2,p1+old_offs,n);    

void*ポインタ演算をサポートしていないためです。 短期的な修正として、計算を行うためにchar*にキャストしました。 これはおそらく正しくありません-Windowsで正しくコンパイルされるようにするためのより良いアイデアがありますか?

私の悪い。 void *でのポインタ演算は不正ですC.char *へのキャストは正しいです。
コード例を更新しました。

64バイトに整列されたnumpy配列を取得するにはどうすればよいですか? https://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.require.htmlの「ALIGNED」アドレスは、いくつかの異なる長さで整列しているようです。 アライメントの長さについてユーザーが設定できるパラメーターはありますか?

Numpyが提供する(非整列)メモリで動作する独自の(Python)整列アロケータを作成しました。

import numpy as np

def empty_aligned(n, align):
    """                                                                                                                                     
    Get n bytes of memory wih alignment align.                                                                                              
    """

    a = np.empty(n + (align - 1), dtype=np.uint8)
    data_align = a.ctypes.data % align
    offset = 0 if data_align == 0 else (align - data_align)
    return a[offset : offset + n]


def test(n):
    for i in xrange(1, 1024):
        b = empty_aligned(n, i)

        try:
            assert b.ctypes.data % i == 0
            assert b.size == n
        except AssertionError:
            print i, b.ctypes.data % i, b.size

おそらく、このようなPythonの回避策は実行可能な解決策ですか?

@eamartinこれは、NumPyの内部Cコードと、f2pyによって生成されたFortranへのインターフェースコード(これもCコード)に関するものです。 明らかな理由で、NumPyの実装はNumPyに依存できません。

ただし、Pythonプロジェクトにはそのトリックを使用できます。

@sturlamolden :しかし、同様の状況で@nachiketや他の人を助けるかもしれません。

@sturlamolden :これがC内部に関するものであることに気付くほど詳しく読んでいませんでした。

ただし、Pythonインターフェースでより優れた整列オプションを提供することは、引数に整列要件があるNumpyとネイティブライブラリ間のPythonインターフェースを開発するのに役立ちます。

私はPythonインターフェースでより良いアライメントオプションを提供することに反対していません🙂

私が提案した「Pythonアラインアロケーター」ソリューションはハックです。 Pythonインターフェースでアライメントを提供するのは良いことだと思いますが、それを行う正しい方法は、経営幹部レベルでアライメントを処理することです。

この機能は私にとって非常に役立ちます。 DMAコントローラーで64バイトの整列データを使用する必要があるFPGAデバイス(Altera A10GX)を使用しています。これにより、コードが40倍高速化されます(!!!)。 @nachiketにも私と同じ問題があるのではないかと@eamartinが使用しているものに似たものを書きましたが、これはちょっとしたハックです。

私は間違いなく64バイトのアライメントをお勧めします:

  1. それはキャッシュラインサイズです
  2. AVX512までのSIMDアライメントに適しています

ここで私たちはほぼ5年後です。

これ(特に64バイトのアライメント)を標準機能にすることについて何か考えはありますか?..

このcythonコードは現在NumPyにあります。 もちろん、これはデフォルトを変更しません。

私の2セント:整列されたアロケーターは、ハードウェアデバイスおよびカーネルレベルの呼び出しとインターフェイスするときに役立ちます。 これらのインターフェイスは、バッファをページに揃えることでメリットが得られる場合があります。

randomgenをマージすることで、 PyArray_realloc_alignedと友達を獲得しました。 これらのルーチンをnumpy/core/includeに移動する必要がありますか?

@mattipさん、それは確かに役に立ちます。 Pythonからもこの機能にアクセスすることは可能でしょうか?

randomgenをマージすることで、 PyArray_realloc_alignedと友達を獲得しました。 これらのルーチンをnumpy/core/includeに移動する必要がありますか?

私のコードをはぎ取ったのですね。 😂

おそらく、randomgenからnumpyへの移行のある時点で迷子になりました。 私
これはあなたのものだという記録があったと思います。

日、2019年11月17日には、午前10時29分Sturla Moldenの[email protected]書きました:

私が書いたコードのロガー寄稿者ではないようです
NumPy🧐:

https://github.com/numpy/numpy/blob/v1.17.2/numpy/_build_utils/src/apple_sgemv_fix.c

https://github.com/numpy/numpy/blob/v1.17.2/numpy/random/src/aligned_malloc/aligned_malloc.h


あなたがコメントしたのであなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312?email_source=notifications&email_token=ABKTSRLWKGXUFE4OK53SJMDQUEMJJA5CNFSM4AYDJQ42YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2
または購読を解除する
https://github.com/notifications/unsubscribe-auth/ABKTSRM7IZIKPGKT4D2W4IDQUEMJJANCNFSM4AYDJQ4Q

コードの元のソースは何でしたか?

リンクは無効ですが、次のように整列されたmallocから適応されました。

https://tianrunhe.wordpress.com/2012/04/23/aligned-malloc-in-c/

Sturlaからのgithubの投稿でした。 元のコードファイルはありませんでした。

日、2019年11月17日には、午前12時04マッティPICUSの[email protected]書きました:

コードの元のソースは何でしたか?


あなたがコメントしたのであなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312?email_source=notifications&email_token=ABKTSRKKJC4K6C4LW4GFYULQUEXNDA5CNFSM4AYDJQ42YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN
または購読を解除する
https://github.com/notifications/unsubscribe-auth/ABKTSRIVMQFEJ5EP227PXL3QUEXNDANCNFSM4AYDJQ4Q

これは、整列されたmalloc用です。

2019年11月17日、日曜日12:14 Kevin Sheppard
書きました:

Sturlaからのgithubの投稿でした。 元のコードファイルはありませんでした。

日、2019年11月17日には、午前12時04マッティPICUSの[email protected]書きました:

コードの元のソースは何でしたか?


あなたがコメントしたのであなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/numpy/numpy/issues/5312?email_source=notifications&email_token=ABKTSRKKJC4K6C4LW4GFYULQUEXNDA5CNFSM4AYDJQ42YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN
または購読を解除する
https://github.com/notifications/unsubscribe-auth/ABKTSRIVMQFEJ5EP227PXL3QUEXNDANCNFSM4AYDJQ4Q

Numpyに50行のコードを提供するすべての人は、専用の著作権ヘッダーを取得しますか? 私は自分の貢献を調べて、何か当てはまるかどうかを確認するかもしれません:-)

NumPy用に書いたコードの寄稿者ではなくなったようです🧐:

あなたは今もこれからもそうです:)

Numpyに50行のコードを提供するすべての人は、専用の著作権ヘッダーを取得しますか? 私は自分の貢献を調べて、何か当てはまるかどうかを確認するかもしれません:-)

いいえ。 ソースコード内でそのようなものをエンコードすることは避けようとします。なぜなら、それは常に非常に不完全であり、維持するのが難しいからです。 私たちは人々にTHANKS.txt自分自身をリストするように頼みます; そのファイルはマージの競合を引き起こすことが多いため、私はそれに対するより良い代替案を探しています。

このページは役に立ちましたか?
0 / 5 - 0 評価