3

画像処理機能を実装しようとしています。ここにあります:

typedef void (*AgFilter)(int*, int*, int*, float*);

static void filter(AndroidBitmapInfo* info, void* pixels, AgFilter func, void* params){

    for(y = 0; y < height; y++){
        for(x = 0; x < width; x++){
            //initizalie r, g, b

            func(&r, &g, &b, params); //here is the problem
        }
    }
}

私はこの関数を次のように渡していますfunc

static inline void brightness(int *r, int *g, int *b, float* param){
    float add = param[0];

    *r += add;
    *g += add;
    *b += add;
}

動作が極端に遅いという問題。なるほど、それは理解できます。しかし、参照によって関数を渡す代わりに、filter(呼び出しの instread func) 内に関数を直接記述すると、はるかに高速に動作します。問題はどこだ?

PSはそうではないことに注意してくださいc++

編集

これは高速に動作します:

static void filter(AndroidBitmapInfo* info, void* pixels, int add){

    for(y = 0; y < height; y++){
        for(x = 0; x < width; x++){
            //initizalie r, g, b
            r += add;
            g += add;
            b += add;
        }
    }
}
4

3 に答える 3

4

問題は、関数をポインターとして渡しているためだと思います。そのため、brightness()はコンパイラによってインライン展開されません。

bright()の定義を filter() 関数にコピーする、目的の結果が得られます-関数をインライン化します。

于 2012-08-28T07:39:11.710 に答える
4

関数の呼び出しには時間がかかります。通常は気付かないかもしれませんが、この関数を 100 万回呼び出します (フル HD 1920x1080 画像の場合、約 200 万回)。最新のカメラは 16 メガピクセルの画像を作成します。各呼び出しに 1 us かかる場合、(実際に本体を実行せずに) 関数を呼び出すための累積時間は 16になります。

どうすれば速くなりますか?いくつかの提案:

  1. 4 つのパラメーターを渡す代わりに、構造体を使用します。

     struct data { int r,g,b; float* param; }
    

    これを一度割り当てて再利用します。funcこれで、単一の引数で呼び出すことができます。

  2. メモリ レイアウトに問題がある可能性があります。paramメモリのどこかにあります。代わりにコピーしstruct dataます:

     struct data { int r,g,b, add; }
    

    この理由は、それparamがメモリ内のどこかにあるためです。つまり、おそらく別のキャッシュ ラインにあるということです。すべてのデータを 1 つの 64 バイト構造に収めることができれば、すべてが 1 つのキャッシュ ラインに収まり、パフォーマンスが大幅に向上します。

    ただし、常にアクセスするため、おそらくあなたの場合ではありませんparam[0]。これは、ランダムな方法で配列にアクセスする場合にさらに問題になります。

  3. スワップ シフトおよびビット マスク操作:

     r = (int) ((line[x] & >> 16 ) & 0xFF);
    

    0xFF3 つの色すべてがマスクされ、コンパイラが定数を一度 CPU レジスタに移動できるようになるため、わずかなブーストを与えることができます。

  4. 関数を呼び出すときは、すべての CPU レジスタを「保存/復元」する必要があります。それには時間がかかります。関数がインライン化されると、コンパイラはどの CPU レジスタが破棄されているかを認識し、それに応じて最適化できます。

    実際には、CPU レジスタは保存されません (少なくとも、私は長い間それを見たことがありません)。最新のコンパイラは、関数を呼び出した後、それらすべてが変更されていると想定しています。

  5. inline関数を直接呼び出すのではなく、参照によって渡すため、効果がないことに注意してください。

  6. スレッドを使用します。これは非常に簡単に並列化できます。データの 1/N で関数を N 回 (CPU コアごとに 1 回) 実行します。これにより、おおよそ N 倍のパフォーマンス向上が得られます。

于 2012-08-28T07:45:29.513 に答える
1

最大の改善点は、ピクセルごとに関数を 1 回呼び出さないようにすることです。brightness関数内でループを移動するのは簡単です。

static inline void brightness(int *r, int *g, int *b, float* param){
    float add = param[0];

    for(y = 0; y < height; y++)
        for(x = 0; x < width; x++){
            //initialize r, g, b
            *r += add;
            *g += add;
            *b += add;
        }
}

さて、作成するすべての異なるフィルター関数内でループ反復コードを複製する必要がないことはわかっているので、これはマクロを使用することが実際に違いを生むケースの 1 つです。このようなことを試してください(テストされていません)。

#define FOR_EACH_PIXEL for(y = 0; y < height; y++) \
                       for(x = 0; x < width;  x++)

static inline void brightness(int *r, int *g, int *b, float* param){
    float add = param[0];

    FOR_EACH_PIXEL 
    {
            //initialize r, g, b
            *r += add;
            *g += add;
            *b += add;
    }

}
于 2012-08-28T08:41:46.097 に答える