1

私はC++で次のようなことを達成しようとしています:

class MyVector; // 3 component vector  class

MyVector const kA = /* ... */;
MyVector const kB = /* ... */;

MyVector const kC = /* ... */;
MyVector const kD = /* ... */;


// I'd like to shorten the remaining lines, ideally making it readable but less code/operations.
MyVector result = kA;

MyVector const kCMinusD = kC - kD;

if(kCMinusD.X <= 0)
{
    result.X = kB.X;
}

if(kCMinusD.Y <= 0)
{
    result.Y = kB.Y;
}

if(kCMinusD.Z <= 0)
{
    result.Z = kB.Z;
}

コードを英語に言い換えると、4 つの「既知の」ベクトルがあります。2 つのベクトルには、結果に必要な値と不要な値があり、必要かどうかは、他の 2 つのベクトルのコンポーネントに基づく分岐に依存します。

いくつかの行列演算とマスキングを使用してこのコードを単純化できるはずだと思いますが、頭を包むことはできません。

今のところ、私はブランチを使用していますが、それでも理解しやすく、コードの冗長性が少ないより良い方法があるかどうかを知りたいと思っています。

編集:

マークのコメントを参照して、ここで何をしようとしているのかを説明します。

このコードは、私が取り組んでいる春の物理学からの抜粋です。コンポーネントは次のとおりです。

kC は現在のスプリングの長さ、kD は最小のスプリング長です。

kA と kB は 2 セットのばね張力であり、その各構成要素は構成要素ごとに一意である可能性があります (つまり、X、Y、または Z に沿った異なるばね張力)。kA は完全に圧縮されていない場合のスプリングの張力であり、kB は完全に圧縮されている場合のスプリングの張力です。

ばねが圧縮されているかどうかに応じて、単純に kC と kD の融合である結果の「ベクトル」を構築したいと思います。

4

4 に答える 4

2

使用しているプラ​​ットフォームによっては、コンパイラは次のようなステートメントを最適化できる場合があります

result.x = (kC.x > kD.x) ? kA.x : kB.x;
result.y = (kC.y > kD.y) ? kA.y : kB.y;
result.z = (kC.z > kD.z) ? kA.z : kB.z;

fsel (浮動小数点選択) 命令または条件付き移動を使用します。個人的には、この方法の方がコードも見栄えが良く、簡潔に見えると思いますが、それは主観的なものです。

コードのパフォーマンスが非常に重要であり、ベクトル クラスを 3 つではなく 4 つの float に変更しても構わない場合は、SIMD (Intel プラットフォームの SSE、PowerPC の VMX など) を使用して比較を行い、回答を選択できます。これを進めると、次のようになります: (疑似コード)

// Set each component of mask to be either 0x0 or 0xFFFFFFFF depending on the comparison
MyVector4 mask = vec_compareLessThan(kC, kD);

// Sets each component of result to either kA or kB's component, depending on whether the bits are set in mask
result = vec_select(kA, kb, mask);

これには慣れるまでに時間がかかり、最初は読みにくいかもしれませんが、最終的には SIMD モードでの考え方に慣れます。

もちろん、通常の警告が適用されます-プロファイリングする前に最適化しないでください。

于 2010-01-15T01:13:16.537 に答える
1

実行時の最適化以上にソースでクリーンな式を探している場合は、「ツールボックス」の観点からこの問題を解決することを検討してください。したがって、MyVectorsignで、gt(より大きい)、およびle(より小さいか等しい)を定義したとしましょう。次に2行で:

MyVector const kSignCMinusD = (kC - kD).sign();
result = kSignCMinusD.gt(0) * kA + kSignCMinusD.le(0) * kB;

演算子のオーバーロードあり:

MyVector const kSignCMinusD = (kC - kD).sign();
result = (kSignCMinusD > 0) * kA + (kSignCMinusD <= 0) * kB;

インスピレーションを得るために、ここにMatLab関数リファレンスがあります。そして明らかに、そのような関数で選択できる多くのC++ベクトルライブラリがあります。

プロファイリングで必要なことが示された場合は、いつでもアクセスしてさらに最適化できます。しかし、多くの場合、最大のパフォーマンスの問題は、全体像をどれだけよく見て、中間計算を再利用できるかということです。

于 2010-01-15T01:56:05.120 に答える
1

ベクター要素が int の場合、次のことができます。

MyVector result;
MyVector const kCMinusD = kC - kD;
int mask = kCMinusD.X >> 31;  // either 0 or -1
result.X = (kB.X & mask) | (kCMinusD.X & ~mask)
mask = kCMinusD.Y >> 31;
result.X = (kB.Y & mask) | (kCMinusD.Y & ~mask)
mask = kCMinusD.Z >> 31;
result.X = (kB.Z & mask) | (kCMinusD.Z & ~mask)

(これは == 0 のケースを別の方法で処理することに注意してください。気にするかどうかはわかりません)

ベクトル要素が int ではなく double の場合、符号ビットが同じ場所にあるため、同様のことができます。整数に変換し、マスクを実行して、元に戻すだけです。

于 2010-01-15T00:24:45.430 に答える
0

減算のみを行っているため、次のように書き直します。

MyVector result;
result.x = kD.x > kC.x ? kB.x : kA.x;
result.y = kD.y > kC.y ? kB.y : kA.y;
result.z = kD.z > kC.z ? kB.z : kA.z;
于 2010-01-15T01:15:30.263 に答える