快速将数组浮动到双数组并返回
Float array to double array and back, quickly
我需要将内存中 float
的大型数组转换为 double
的数组并返回。 Visual C++ 15 update 3 中是否有任何 SSE 编译器内在函数有帮助?
编辑:这是两种有线格式之间的转换,因此#define 无济于事。数据结构存储为浮点数,但第三方处理库需要一个双精度数组。
您可以为此使用 SSE:
float
-> double
: _mm_cvtps_pd
double
-> float
: _mm_cvtpd_ps
首先尝试一个简单的标量循环,因为 (a) 编译器可能无论如何都会为您矢量化,并且 (b) 您很可能受内存限制,因此 SIMD 优化可能没有多大帮助。
A data structure is stored as floats, but a third party processing library expects an array of double.
它可以处理缓存大小的块吗?
如果它没有卡在第 3 方库中,最好的办法是即时转换,使用 _mm_cvtps_pd
从一对浮点数加载一对双打,然后类似地存储回去浮动,所以你永远不会在内存中有一个 double
的数组。
但如果你不能这样做,你至少可以在读取一些浮点数并写入一些双精度数后,在数据在 L1 或 L2 缓存中仍然很热的时候将数据提供给库。
实际上,如果它是 "wire format",那么大概数据必须首先通过 CPU 到达内存,除非你有一个零拷贝接收 API DMA 直接进入您的缓冲区。当您收到每个数据包时,转换的理想位置可能是小块。直接复制并转换为 double
,或者如果您还需要原始 float
数据,则复制到 float
和 double
数组。
这不是您问题的实际答案,而只是一个如何使 ALU 仅对转换起作用的示例。如果实施得当,您可以将其与 FPU cast 并行以获得更快的速度。此解决方案应 100% 兼容 IEEE。
更新:我使它更慢且更易读,但与 IEEE 兼容,因为英特尔在第三代 i7 中实现了它(甚至 NAN 转换都是二进制等价的)
#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();
}
我需要将内存中 float
的大型数组转换为 double
的数组并返回。 Visual C++ 15 update 3 中是否有任何 SSE 编译器内在函数有帮助?
编辑:这是两种有线格式之间的转换,因此#define 无济于事。数据结构存储为浮点数,但第三方处理库需要一个双精度数组。
您可以为此使用 SSE:
float
-> double
: _mm_cvtps_pd
double
-> float
: _mm_cvtpd_ps
首先尝试一个简单的标量循环,因为 (a) 编译器可能无论如何都会为您矢量化,并且 (b) 您很可能受内存限制,因此 SIMD 优化可能没有多大帮助。
A data structure is stored as floats, but a third party processing library expects an array of double.
它可以处理缓存大小的块吗?
如果它没有卡在第 3 方库中,最好的办法是即时转换,使用 _mm_cvtps_pd
从一对浮点数加载一对双打,然后类似地存储回去浮动,所以你永远不会在内存中有一个 double
的数组。
但如果你不能这样做,你至少可以在读取一些浮点数并写入一些双精度数后,在数据在 L1 或 L2 缓存中仍然很热的时候将数据提供给库。
实际上,如果它是 "wire format",那么大概数据必须首先通过 CPU 到达内存,除非你有一个零拷贝接收 API DMA 直接进入您的缓冲区。当您收到每个数据包时,转换的理想位置可能是小块。直接复制并转换为 double
,或者如果您还需要原始 float
数据,则复制到 float
和 double
数组。
这不是您问题的实际答案,而只是一个如何使 ALU 仅对转换起作用的示例。如果实施得当,您可以将其与 FPU cast 并行以获得更快的速度。此解决方案应 100% 兼容 IEEE。
更新:我使它更慢且更易读,但与 IEEE 兼容,因为英特尔在第三代 i7 中实现了它(甚至 NAN 转换都是二进制等价的)
#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();
}