2017-10-24 5 views
0

現在、OpenCLコードを最適化しようとしています。私は値を返すためにいくつかのポインタを使用します。レジスタに格納された一時変数を作成する方が効率的ですか(コンパイラがこれを最適化するので、少なくとも私は推測します)か、ポインタに直接アクセスして関数全体で使用できますか?OpenCL関数でのポインタアクセスの最適化(ポインタを使用して)参照渡しのパラメータ

私はシンプルな箱の線の交差点で、あなたに例を示しましょう:

TMINとTMAXは、一時的な変数に格納されています。

bool intersect(const Ray* ray, float3 boxmin, float3 boxmax, float* tmin, float* tmax) 
{ 
    float3 invR = 1.0f/ray->dir; 

    float t1 = (boxmin.x - ray->origin.x) * invR.x; 
    float t2 = (boxmax.x - ray->origin.x) * invR.x; 
    float t3 = (boxmin.y - ray->origin.y) * invR.y; 
    float t4 = (boxmax.y - ray->origin.y) * invR.y; 
    float t5 = (boxmin.z - ray->origin.z) * invR.z; 
    float t6 = (boxmax.z - ray->origin.z) * invR.z; 

    float tmin_ = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6)); 
    float tmax_ = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6)); 

    if (tmax_ < 0) 
    return false; 
    if (tmin_ > tmax_) 
    return false; 

    *tmax = tmax_; 
    *tmin = tmin_; 
    return true; 
} 

直接アクセス:

bool intersect(const Ray* ray, float3 boxmin, float3 boxmax, float* tmin, float* tmax) 
{ 
    float3 invR = 1.0f/ray->dir; 

    float t1 = (boxmin.x - ray->origin.x) * invR.x; 
    float t2 = (boxmax.x - ray->origin.x) * invR.x; 
    float t3 = (boxmin.y - ray->origin.y) * invR.y; 
    float t4 = (boxmax.y - ray->origin.y) * invR.y; 
    float t5 = (boxmin.z - ray->origin.z) * invR.z; 
    float t6 = (boxmax.z - ray->origin.z) * invR.z; 

    *tmin = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6)); 
    *tmax = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6)); 

    if (*tmax < 0) 
    return false; 
    if (*tmin > *tmax) 
    return false; 
    return true; 
} 

をまあ、それはありません最も良い例は、3つの逆参照されたポインターがあるためです。パフォーマンスに差はないかもしれませんが、私が意味することを実証するだけです。 十分な知識を持っている人がいますか?この場合、ほとんどのOpenCLコンパイラは何をしていますか? このような場合にC/C++コンパイラが何をするのか知ることも興味深いでしょう。

+0

どちらが高速ですか?はい。 1つは速いです。多分。 2つをプロファイルします。それぞれのベンチマーク2つのバージョンが生成するマシンコードを見てください。要するに、お互いに対して2つをテストする。 –

答えて

0

ポインタのメモリ位置によって異なります(tmaxおよびtmin)。

  • プライベート:コンパイラはおそらくそれらを最適化します。
  • ローカル:コンパイラは、ポインタが常にグループ内の単一の作業項目によって指し示されていることをコンパイラが明確にしない限り、それらを最適化できません。 (そしてそれでもやっていなくても)。ケース2の場合、一部の劣化が生じる場合があります。
  • グローバル:グローバルメモリが他のワークグループやCPU側で変更されているかどうかはわかりません。 2番目のケースではペナルティと余分なメモリアクセスがあります。

パフォーマンスの最適なソリューションが最初のものです。ほとんどのチェックはプライベートスペースで行い、必要なときにのみメモリへのポインタにアクセスします。余分な変数の使用について心配する必要はありません.GPUには十分なレジスタがあります。

float tmin_ = fmax(fmax(fmin(t1, t2), fmin(t3, t4)), fmin(t5, t6)); 
    float tmax_ = fmin(fmin(fmax(t1, t2), fmax(t3, t4)), fmax(t5, t6)); 

    if (tmax_ < 0) 
    return false; 
    if (tmin_ > tmax_) 
    return false; 

    *tmax = tmax_; 
    *tmin = tmin_; 
0

これらの2つのオプションの間には、まともなコンパイラによるパフォーマンスの違いはありません。ほとんどのOpenCLの実装はllvmに基づいており、絶対にこのようなtempsを最適化します。

これは、遺伝子的に言えば唯一の簡単な方法は、両方のオプションを実行することです。これは常に最適化の証明ですが、CPUベースのコンパイラではアセンブリの出力もよく見かけることがあります。それはOpenCLを使うのが難しいです。

メモこのコードをはるかに簡潔にするために、ベクトル演算を使用することができます。それがパフォーマンスを助けるかどうかは別の話です。

また、ポインタを使用しないようにするための構造体を返すこともできます。

+0

答えに感謝します。 コンパイラは本当に賢いと思っていました。コンパイラに向かって最適化することは有望だろう。おそらく、いくつかのテストを開始し、さまざまな理論上の最適化が違いを生むかどうかを確認します。 さて、C++で作成したプロジェクトのコードをコピーしたばかりですが、ベクトル操作でコードを最適化することは可能です。私はそれを間違いなくします。 –

+0

最初にやりたいことは、明示的なメモリ階層を最適化することです。 GPUには実際にプロファイリングツールを使用して、パフォーマンスの最適化に経験的なアプローチが必要です。 CPUもそうですが、多くの人が臨時の方法で逃げるようです。 –

関連する問題