6

ホストに問題があることがわかりました-OpenCLのクライアントフロート標準。問題は、x86でコンパイルするときに、Openclによって計算された浮動小数点が私のVisualStudio2010コンパイラと同じ浮動小数点制限内にないことでした。ただし、x64でコンパイルする場合、それらは同じ制限内にあります。私はそれが何かでなければならないことを知っています、http://www.viva64.com/en/b/0074/

テスト中に使用したソースは次のとおりです。http://www.codeproject.com/Articles/110685/Part-1-OpenCL-Portable-Parallelism プログラムをx86で実行すると、202個の等しい数値が得られます。カーネルとC++プログラムは、1269760の数値の2乗を取りました。ただし、64ビットビルドでは、1269760の数値が正しかった、つまり100%でした。さらに、openclとx86 c ++の計算結果の誤差は5.5385384e-014であることがわかりました。これは、数値のイプシロンである2.92212543378266922312416e-19と比較すると、非常に小さいですが、十分に小さくはありません。
これは、プログラムが2つの数値を1つの等しい数値として認識できるように、エラーをイプシロンよりも小さくする必要があるためです。もちろん、通常、フロートをネイティブに比較することはありませんが、フロートの制限が異なることを知っておくとよいでしょう。はい、flt:staticを設定しようとしましたが、同じエラーが発生しました。

ですから、この振る舞いについて何らかの説明が必要です。すべての回答を事前に感謝します。

4

2 に答える 2

10

プロジェクトをx86から​​x64に切り替えても、GPUコードは何も変わらないため、CPUでの乗算の実行方法と同じようにすべてを行う必要があります。x86モードとx64モードでの浮動小数点数の処理には微妙な違いがあります。最大の違いは、x64 CPUはSSEとSSE2もサポートしているため、Windowsの64ビットモードでの数学演算にデフォルトで使用されることです。

HD4770 GPUは、単精度浮動小数点ユニットを使用してすべての計算を実行します。一方、最新のx64 CPUには、浮動小数点数を処理する2種類の機能ユニットがあります。

  • 80ビットのはるかに高い拡張精度で動作するx87FPU
  • 32ビットおよび64ビットの精度で動作し、他のCPUが浮動小数点数を処理する方法と多くの互換性があるSSEFPU

32ビットモードでは、コンパイラはSSEが使用可能であるとは想定せず、計算を行うために通常のx87FPUコードを生成します。この場合、のような操作data[i] * data[i]は、はるかに高い80ビットの精度を使用して内部で実行されます。種類の比較はif (results[i] == data[i] * data[i])次のように実行されます。

  • data[i]を使用してx87FPUスタックにプッシュされますFLD DWORD PTR data[i]
  • data[i] * data[i]を使用して計算されますFMUL DWORD PTR data[i]
  • result[i]を使用してx87FPUスタックにプッシュされますFLD DWORD PTR result[i]
  • 両方の値はを使用して比較されますFUCOMPP

ここに問題があります。data[i] * data[i]80ビット精度のx87FPUスタック要素に存在します。result[i]32ビット精度のGPUから来ています。data[i] * data[i]有効桁数がはるかに多いのに対しresult[i]、ゼロがたくさんある(80ビット精度)ため、両方の数値が異なる可能性があります。

64ビットモードでは、別の方法で処理が行われます。コンパイラは、CPUがSSE対応であることを認識しており、SSE命令を使用して計算を行います同じ比較ステートメントがx64で次のように実行されます。

  • data[i]を使用してSSEレジスタにロードされますMOVSS XMM0, DWORD PTR data[i]
  • data[i] * data[i]を使用して計算されますMULSS XMM0, DWORD PTR data[i]
  • result[i]を使用して別のSSEレジスタにロードされますMOVSS XMM1, DWORD PTR result[i]
  • 両方の値はを使用して比較されますUCOMISS XMM1, XMM0

この場合、二乗演算は、GPUで使用されるのと同じ32ビット単精度で実行されます。80ビット精度の中間結果は生成されません。そのため、結果は同じです。

GPUが関与していなくても、これを実際にテストするのは非常に簡単です。次の簡単なプログラムを実行するだけです。

#include <stdlib.h>
#include <stdio.h>

float mysqr(float f)
{
    f *= f;
    return f;
}

int main (void)
{
    int i, n;
    float f, f2;

    srand(1);
    for (i = n = 0; n < 1000000; n++)
    {
        f = rand()/(float)RAND_MAX;
        if (mysqr(f) != f*f) i++;
    }
    printf("%d of %d squares differ\n", i);
    return 0;
}

mysqr中間の80ビットの結果が32ビットの精度で変換されるように特別に記述されていますfloat。コンパイルして64ビットモードで実行すると、出力は次のようになります。

0 of 1000000 squares differ

コンパイルして32ビットモードで実行すると、出力は次のようになります。

999845 of 1000000 squares differ

原則として、32ビットモードで浮動小数点モデルを変更できるはずです(プロジェクトプロパティ->構成プロパティ-> C / C ++->コード生成->浮動小数点モデル)が、少なくともVS2010中間では、変更しても何も変更されません。結果は引き続きFPUに保持されます。実行できることは、計算された正方形のストアとリロードを強制して、GPUからの結果と比較するに32ビットの精度に丸められるようにすることです。上記の簡単な例では、これは次のように変更することで実現されます。

if (mysqr(f) != f*f) i++;

if (mysqr(f) != (float)(f*f)) i++;

変更後、32ビットコードの出力は次のようになります。

0 of 1000000 squares differ
于 2012-06-26T14:09:39.653 に答える
-1

私の場合

(float)(f*f)

助けにはならなかった。使用しました

  correct = 0;
  for(unsigned int i = 0; i < count; i++) {
    volatile float sqr = data[i] * data[i];
    if(results[i] == sqr)
      correct++;
  }

代わりは。

于 2012-09-11T22:59:49.980 に答える