float
の大きな配列をdouble
の配列に変換して戻す必要があります。 Visual C++ 15のアップデート3にSSEコンパイラ組み込み関数がありますか?浮動小数点配列を倍精度浮動小数点配列に変換してすぐに戻す
EDIT:これは2つのワイヤフォーマット間の変換であるため、#defineは役に立ちません。データ構造は浮動小数点数として格納されますが、第三者の処理ライブラリではdoubleの配列が必要です。
float
の大きな配列をdouble
の配列に変換して戻す必要があります。 Visual C++ 15のアップデート3にSSEコンパイラ組み込み関数がありますか?浮動小数点配列を倍精度浮動小数点配列に変換してすぐに戻す
EDIT:これは2つのワイヤフォーマット間の変換であるため、#defineは役に立ちません。データ構造は浮動小数点数として格納されますが、第三者の処理ライブラリではdoubleの配列が必要です。
あなたはこのためにSSEを使用することができます。
float
- >double
:_mm_cvtps_pd
double
- >float
:(a)として、コンパイラはベクトル化するかもしれないが_mm_cvtpd_ps
は、まず単純なスカラーループを試してみてくださいとにかく(b)あなたは記憶に縛られているかもしれないので、SIMDの最適化はあまり役に立たないかもしれません。
これは便利ですが、128ビットのXMMレジスタで動作しますか?つまり、コマンドごとに2つの値しかありません。代わりにYMMを使用して4つの値を扱うような指示がありますか? –
もちろん、AVX相当の[_mm256_cvtps_pd](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_cvtps_pd&expand=1680,1680)と[_mm256_cvtpd_ps](https://ソフトウェア)を参照してください。 intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_cvtpd_ps&expand=1680,1680,1621)、上記の理由により、これらがSSEバージョンよりも大幅に改善されるとは限りません。 –
データ構造は浮動小数点数として格納されますが、サードパーティの処理ライブラリでは2倍の配列が必要です。
キャッシュサイズのチャンクで処理できますか?
サードパーティ製のライブラリでスタックされていない場合、最善のことは、オンザフライで変換し、_mm_cvtps_pd
の浮動小数点数から二重のペアをロードし、同様にフロートに戻すことですメモリにdouble
の配列を持つことは決してありません。
しかし、それができないのであれば、フロートを読んだり、倍を書き込んだりすると、少なくともL1またはL2キャッシュで熱いうちに、データをライブラリにフィードできます。
実際には、「ワイヤフォーマット」の場合、バッファにDMAが正しく転送されるゼロコピー受信APIを使用していない限り、おそらくデータは最初にメモリに転送されます。変換のための理想的な場所は、各パケットを受け取るときに小さな塊であるかもしれません。変換を直接double
にコピーするか、元のfloat
データが必要な場合はfloat
とdouble
の両方の配列にコピーしてください。
ライブラリには完全な配列が必要です。私はソースを持っていない、私は読んで、その場で変換します。それでも、1つの価格の2つです。 –
@SevaAlekseyev:あなたのデータがキャッシュとRAM間を移動する回数を最小限に抑えるためにできることは何でも。あなたの配列が巨大であれば、変換ループにNTストアを使う価値があります。または、floatとdoubleの両方を書いているときに、NTストアを使用して2番目に使用されるストアを使用していますか?しかし、すべてがL3に収まるなら、それをしないでください。 –
これはあなたの質問に対する実際の回答ではなく、変換でALUのみを動作させる方法の例です。 FPUキャストと並列化することで、適切に実装すればスピードを上げることができます。このソリューションは、100%IEEE互換である必要があります。
更新:私はこれより遅いと読みやすくするが、インテルとしてIEEE互換性が(たとえNAN変換がバイナリequaleある点まで)I7の第3世代で実装
#include <iostream>
#include <chrono>
#include <math.h>
void toDouble(float *inData, double *outData, int count)
{
if (count % 2)
{
std::cout << "Error count must be divided by 2" << std::endl;
return;
}
unsigned long long *pfData = (unsigned long long *)(inData);
unsigned long long *pdData = (unsigned long long *)(outData);
unsigned long long *pfDataEnd = pfData + count/2;
for (int i = 0; pfData<pfDataEnd; pfData++, pdData++, i += 2)
{
unsigned long long cl;
unsigned long long S1 = (*pfData & 0x80000000ull) << 32;
unsigned long long fE1 = (*pfData & 0x7F800000ull) << 32;
unsigned long long F1 = (*pfData & 0x007FFFFFull) << 29;
for (cl = 0; !fE1 && F1 && !(F1 & 0x7FF0000000000000ull); cl++)
F1 <<= 1;
if (cl > 0)
cl--;
unsigned long long dE1 = (fE1 == 0x7F80000000000000ull) ? 0x7FF0000000000000 : ((fE1 | F1) ? (fE1 >> 3) + 0x3800000000000000ull - cl * 0x0010000000000000ull : 0ull);
F1 &= 0x000FFFFFFFFFFFFFull;
*pdData = S1 | dE1 | F1;
pdData++;
unsigned long long S2 = *pfData & 0x8000000000000000ull;
unsigned long long fE2 = (*pfData & 0x7F80000000000000ull);
unsigned long long F2 = (*pfData & 0x007FFFFF00000000ull) >> 3;
for (cl = 0; !fE2 && F2 && !(F2 & 0x7FF0000000000000ull); cl++)
F2 <<= 1;
if (cl > 0)
cl--;
unsigned long long dE2 = (fE2==0x7F80000000000000ull) ? 0x7FF0000000000000 : ((fE2 | F2) ? (fE2 >> 3) + 0x3800000000000000ull - cl * 0x0010000000000000ull : 0ull);
F2 &= 0x000FFFFFFFFFFFFFull;
*pdData = S2 | dE2 | F2;
if (i == 126)
continue;
}
}
void toFloat(double *inData, float *outData, int count)
{
if (count % 2)
{
std::cout << "Error count must be divided by 2" << std::endl;
return;
}
unsigned long long *pdData = (unsigned long long *)(inData);
unsigned long long *pfData = (unsigned long long *)(outData);
unsigned long long *pfDataEnd = pfData + count/2;
for (int i=0; pfData<pfDataEnd; pfData++, pdData+=2,i+=2)
{
unsigned long long S1 = (*pdData & 0x8000000000000000ull);
unsigned long long dE1 = (*pdData & 0x7FF0000000000000ull);
unsigned long long fE1 = (dE1 <= 0x3800000000000000ull) ? 0ull : ((dE1 >= 0x4800000000000000ull) ? 0x0FF0000000000000ull : (dE1 - 0x3800000000000000ull));
unsigned long long F1 = (dE1 <= 0x3800000000000000ull) ? ((dE1 < 0x3600000000000000ull) ? 0ull : ((*pdData & 0x000FFFFFFFFFFFFFull | 0x0010000000000000ull) >> ((0x3800000000000000ull - dE1 >> 52) + 1))) : ((dE1 >= 0x47F0000000000000ull) ? (((dE1 == 0x7FF0000000000000ull) && (*pdData & 0x000FFFFFFFFFFFFFull)) ? 0x0008000000000000ull : 0ull) : (*pdData & 0x000FFFFFFFFFFFFFull));
F1 += (((F1 & 0x0000000010000000ull) && ((F1 & 0x0000000020000000ull) || (F1 & 0x000000000FFFFFFFull))) ? 0x0000000020000000ull : 0ull); //rounding
fE1 += F1 & 0x7FF0000000000000ull;
F1 &= 0x000FFFFFE0000000ull;
unsigned long long S2 = (*(pdData+1) & 0x8000000000000000ull);
unsigned long long dE2 = (*(pdData+1) & 0x7FF0000000000000ull);
unsigned long long fE2 = (dE2 <= 0x3800000000000000ull) ? 0ull : ((dE2 >= 0x4800000000000000ull) ? 0x0FF0000000000000ull : (dE2 - 0x3800000000000000ull));
unsigned long long F2 = (dE2 <= 0x3800000000000000ull) ? ((dE2 < 0x3600000000000000ull) ? 0ull : ((*(pdData + 1) & 0x000FFFFFFFFFFFFFull | 0x0010000000000000ull) >> ((0x3800000000000000ull - dE2 >> 52) + 1))) : ((dE2 >= 0x47F0000000000000ull) ? (((dE2 == 0x7FF0000000000000ull) && (*(pdData+1) & 0x000FFFFFFFFFFFFFull)) ? 0x0008000000000000ull : 0ull) : (*(pdData + 1) & 0x000FFFFFFFFFFFFFull));
F2 += (((F2 & 0x0000000010000000ull) && ((F2 & 0x0000000020000000ull) || (F2 & 0x000000000FFFFFFFull))) ? 0x0000000020000000ull : 0ull); //rounding
fE2 += F2 & 0x7FF0000000000000ull;
F2 &= 0x000FFFFFE0000000ull;
*pfData = S2 | ((fE2 | F2) << 3) | ((S1 | ((fE1 | F1) << 3)) >> 32);
if (i == 88)
continue;
}
}
int valTestFtoD(float *inData, double *outData, int count)
{
for (int i = 0; i < count; i++)
{
if ((((double)inData[i]) != outData[i]) && ((inData[i] == inData[i]) || (outData[i] == outData[i])))
return i;
}
return -1;
}
int valTestDtoF(double *inData, float*outData, int count)
{
for (int i = 0; i < count; i++)
{
if ((((float)inData[i]) != outData[i]) && ((inData[i] == inData[i]) || (outData[i] == outData[i])))
return i;
}
return -1;
}
void testFloatToDouble()
{
std::cout << "\nSTART Float to Double TEST\n";
int elemNum = 1024 * 1024 * 8;
float *f_arr = new float[elemNum];
double *d_arr = new double[elemNum];
auto start = std::chrono::steady_clock::now();
f_arr[0] = 2.0f;
for (int i = 1; i < elemNum; i++)
{
f_arr[i] = i/f_arr[i - 1];
d_arr[i] = 0.0f;
}
long long duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "init of floats and doubles done in " << duration << std::endl;
start = std::chrono::steady_clock::now();
for (int i = 0; i < elemNum; i++)
{
d_arr[i] = f_arr[i];
}
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "cast to double done in " << duration << std::endl;
start = std::chrono::steady_clock::now();
float pi = 3.14159265358979323846;
float e = 2.71828182845904523536;
f_arr[0] = pi;
d_arr[0] = 0.0;
for (int i = 1; i < elemNum; i++)
{
f_arr[i] = (e + i)/f_arr[i - 1];
d_arr[i] = 0.0;
}
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "init of floats and doubles done in " << duration << std::endl;
start = std::chrono::steady_clock::now();
toDouble(f_arr, d_arr, elemNum);
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "toDouble done in " << duration << std::endl;
std::cout << "toDouble validation test ";
int errorPos = valTestFtoD(f_arr, d_arr, elemNum);
if (errorPos < 0)
std::cout << "OK" << std::endl;
else
{
std::cout << "FAIL at " << errorPos << std::endl;
std::cout << "float [" << errorPos << "]= " << f_arr[errorPos] << std::endl;
std::cout << "double[" << errorPos << "]= " << d_arr[errorPos] << std::endl;
}
delete[] f_arr;
delete[] d_arr;
std::cout << "END TEST\n";
}
void testDoubleToFloat()
{
std::cout << "\nSTART Double to Float TEST\n";
int elemNum = 1024 *1024 * 8;
float *f_arr = new float[elemNum];
double *d_arr = new double[elemNum];
auto start = std::chrono::steady_clock::now();
d_arr[0] = 2.0f;
for (int i = 1; i < elemNum; i++)
{
d_arr[i] = i/d_arr[i - 1];
f_arr[i] = 0.0f;
}
long long duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "init of floats and doubles done in " << duration << std::endl;
start = std::chrono::steady_clock::now();
for (int i = 0; i < elemNum; i++)
{
f_arr[i] = (float)d_arr[i];
}
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "cast to float done in " << duration << std::endl;
start = std::chrono::steady_clock::now();
double pi = 3.14159265358979323846;
double e = 2.71828182845904523536;
d_arr[0] = pi;
f_arr[0] = 0.0f;
for (int i = 1; i < elemNum; i++)
{
d_arr[i] = (e+i)/d_arr[i - 1];
f_arr[i] = 0.0f;
}
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "init of floats and doubles done in " << duration << std::endl;
start = std::chrono::steady_clock::now();
toFloat(d_arr, f_arr, elemNum);
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
std::cout << "toFloat done in " << duration << std::endl;
std::cout << "toFloat validation test ";
int errorPos = valTestDtoF(d_arr, f_arr, elemNum);
if (errorPos < 0)
std::cout << "OK" << std::endl;
else
{
std::cout << "FAIL at " << errorPos << std::endl;
std::cout << "double[" << errorPos << "]= " << d_arr[errorPos] << std::endl;
std::cout << "float[" << errorPos << "]= " << f_arr[errorPos] << std::endl;
}
delete[] f_arr;
delete[] d_arr;
std::cout << "END TEST\n";
}
int main()
{
testFloatToDouble();
testDoubleToFloat();
}
これはあまりにも多くのALU命令がやる価値があることです。 (https://godbolt.org/g/1EURvH)。私は手動でベクトル化して64ビットから128ビットに変換しても(パック化された変換命令のように)、FPU変換ではなく実行リソースを使用することで何のサイクルも費やす価値がないと思います。 'CVTPS2PD xmm、m64'はハスウェル(ポート0とロードポート)で2 uopsであり、1クロックで1スループットです。 ymm、m128バージョンと同じです。したがって、インテルのCPUは、すでに格納できるほど速く変換できます(クロックごとに1つのベクトル結果)。 (ソース:http://agner.org/optimize/) –
これをロード/変換/ストアループに混ぜると、x86では速度が遅くなります。 –
@PeterCordesおそらくx64で2倍遅くて普通に変換されているようにあなたは正しいでしょう。私はそれが解決策ではないと言いましたので、あなたがそれを行うことができる例です。アセンブリレベルで最適化するための適切なスキルはありませんが、浮動小数点演算と整数演算を1つのコアで並列化できることはわかっています。これは、そうしたソリューションを使用できると考えるための基礎になります。 – Logman
'# double floatを定義する、またはその逆を定義する – LogicStuff
すべての単一値をキャストできないようにする方法はありません。 –
変換時にわずかな損失が発生する可能性がありますか?それ以外の場合は、一方向に変換し、元のままにして変換バックを保存します。 –