あまりにも具体的な答えかもしれませんが、NEONをサポートするARMプラットフォームでは、ストライドコピーをさらに高速化するためにNEONベクトル化を使用できます。これは、リソースが比較的限られている環境では人生を節約することができます。これはおそらく、最初にARMをその環境で使用する理由です。目立った例は、ほとんどのデバイスがNEONをサポートするARM v7aアーキテクチャを引き続き使用するAndroidです。
次の例では、YUV420spイメージの半平面UV平面をYUV420pイメージの平面UV平面にコピーするループを示しています。送信元バッファと宛先バッファのサイズは、両方とも640*480/2
バイトです。すべての例は、Android NDK r9dのg ++ 4.8でコンパイルされています。
レベル1:彼らはサムスンExynosオクタ5420プロセッサ上で実行されているだけ-O3
でコンパイル正規
void convertUVsp2UVp(
unsigned char* __restrict srcptr,
unsigned char* __restrict dstptr,
int stride)
{
for(int i=0;i<stride;i++){
dstptr[i] = srcptr[i*2];
dstptr[i + stride] = srcptr[i*2 + 1];
}
}
、平均で約1.5ミリ秒かかります。
レベル2:広げとは-O3
のみ、平均で約1.15ミリ秒かかるとコンパイル移動ポインタ
void convertUVsp2UVp(
unsigned char* __restrict srcptr,
unsigned char* __restrict dstptr,
int stride)
{
unsigned char* endptr = dstptr + stride;
while(dstptr<endptr){
*(dstptr + 0) = *(srcptr + 0);
*(dstptr + stride + 0) = *(srcptr + 1);
*(dstptr + 1) = *(srcptr + 2);
*(dstptr + stride + 1) = *(srcptr + 3);
*(dstptr + 2) = *(srcptr + 4);
*(dstptr + stride + 2) = *(srcptr + 5);
*(dstptr + 3) = *(srcptr + 6);
*(dstptr + stride + 3) = *(srcptr + 7);
*(dstptr + 4) = *(srcptr + 8);
*(dstptr + stride + 4) = *(srcptr + 9);
*(dstptr + 5) = *(srcptr + 10);
*(dstptr + stride + 5) = *(srcptr + 11);
*(dstptr + 6) = *(srcptr + 12);
*(dstptr + stride + 6) = *(srcptr + 13);
*(dstptr + 7) = *(srcptr + 14);
*(dstptr + stride + 7) = *(srcptr + 15);
srcptr+=16;
dstptr+=8;
}
}
で少し絞っ。これはおそらく、他の答えに従って、通常のアーキテクチャーと同じくらい速いでしょう。
レベル3:-O3 -mfpu=neon -ftree-vectorize -ftree-vectorizer-verbose=1 -mfloat-abi=softfp
でコンパイル +レギュラーGCC自動NEONベクトル化
void convertUVsp2UVp(
unsigned char* __restrict srcptr,
unsigned char* __restrict dstptr,
int stride)
{
for(int i=0;i<stride;i++){
dstptr[i] = srcptr[i*2];
dstptr[i + stride] = srcptr[i*2 + 1];
}
}
、平均して約0.6ミリ秒を要します。参考のためにmemcpy
の640*480
バイト、またはここでテストされた量の2倍の平均で約0.6ミリ秒かかる。
上記のNEONパラメータを使用してコンパイルされた2番目のコード(展開されていない点)は、ほぼ同じ時間0.6 msを要します。
ループの標準は、少なくともループの標準と同じ速さで実行されます。皮肉なことに、それはあなたが使用しているデータ記憶構造に依存します。配列の場合、私はあなたのモジュラスで増分されたforループよりも優れているとは思いません。 – ChrisCM
'memcpy'は、それが動作しているメモリが連続しているために実行できる最適化のために、' for'ループより速いことがあります。これらの最適化はここではできません。 –
@dauphicしかし、なぜCUDAにピッチをコピーする 'cudaMemcpy2D'があるのですか? – JackOLantern