我需要将内存中的float
大数组转换为double
的数组,然后返回。 Visual C++ 15更新3中是否有任何SSE编译器内部函数有帮助?Float array double array and back,
编辑:它是两种线格式之间的转换,所以#define不会帮助。数据结构以浮点形式存储,但第三方处理库需要双精度数组。
我需要将内存中的float
大数组转换为double
的数组,然后返回。 Visual C++ 15更新3中是否有任何SSE编译器内部函数有帮助?Float array double array and back,
编辑:它是两种线格式之间的转换,所以#define不会帮助。数据结构以浮点形式存储,但第三方处理库需要双精度数组。
您可以使用SSE此:
float
- >double
:_mm_cvtps_pd
double
- >float
:_mm_cvtpd_ps
首先尝试一个简单的标量循环,但正如(一)编译器可向量化无论如何,(b)你可能会受到内存限制,所以SIMD优化可能没有多大帮助。
这很有用,但它适用于128位XMM寄存器,不是吗?也就是说,每个命令只有两个值。那些使用YMM代替并处理4个值的指令是否有味道? –
是的,当然,请参阅AVX等价物[_mm256_cvtps_pd](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_cvtps_pd&expand=1680,1680)和[_mm256_cvtpd_ps](https:// software。 intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_cvtpd_ps&expand=1680,1680,1621),但请注意,由于上述原因,这些可能无法为SSE版本带来任何显着改进。 –
数据结构存储为浮点数,但第三方处理库需要double数组。
它可以处理缓存大小的块吗?
如果它没有卡在第三方库中,最好的办法是进行转换,从一对漂浮物中加载一对双打和_mm_cvtps_pd
,同样存储回浮标,这样你从来没有在内存中的double
阵列。
但是,如果你不能做到这一点,你至少可以将数据反馈到库,同时它还是在L1或L2缓存中的热点阅读一些花车和写一些双打后。实际上,如果它是一个“有线格式”,那么大概数据必须首先通过CPU到内存的路径,除非你有一个零拷贝接收API,DMA直接进入你的缓冲区。收到每个数据包时,转换的理想位置可能会很小。无论是与转换直接复制到double
,或复制到两个float
和double
阵列,如果你还需要原始float
数据。
库需要一个完整的数组。我没有来源,我阅读并随时转换。仍然,两个为一个的价格:) –
@SevaAlekseyev:是的,无论你可以做什么,以尽量减少你的数据在缓存和RAM之间移动的次数。如果你的数组很大,那么在转换循环中使用NT商店可能是值得的。或者,也许同时写入浮动和双重,使用NT商店为将被使用的第二个?但是,如果一切都适合L3,不要这样做。 –
这是不是一个实际的回答你的问题,而只是一个例子如何使转换只ALU工作。如果您正确实施它,您可以将它与FPU cast并行以获得更多速度。此解决方案应该100%IEEE兼容。
更新:我做这个慢,更具可读性,但英特尔执行它的第三代酷睿i7的(到连南转换为二进制equale点)
#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'在Haswell(端口0和一个加载端口)上是2个微处理器,每时钟吞吐量为一个。 ymm,m128版本也一样。因此,Intel CPU可以尽可能快地进行转换(每个时钟一个矢量结果)。 (来源:http://agner.org/optimize/) –
将这个混合到一个加载/转换/存储循环只会降低它在x86上的速度。 –
@PeterCordes也许你是对的,因为它比x64上的正常转换慢2倍,正如我所说这不是一个解决方案,它只是一个例子,你可以做到这一点。我也没有适当的技巧来在汇编级别优化它,但我知道你可以在单核上并行浮动和整数操作,这是我认为他可以使用这种解决方案的基础。 – Logman
'#IEEE兼容定义双浮动“或反之亦然 – LogicStuff
无法为您节省铸造每一个值。 –
您是否希望在转换回来时获得轻微的损失?否则,只需转换一种方式并保留原始文件以保存转换。 –