2016-11-16 73 views
2

我需要将内存中的float大数组转换为double的数组,然后返回。 Visual C++ 15更新3中是否有任何SSE编译器内部函数有帮助?Float array double array and back,

编辑:它是两种线格式之间的转换,所以#define不会帮助。数据结构以浮点形式存储,但第三方处理库需要双精度数组。

+2

'#IEEE兼容定义双浮动“或反之亦然 – LogicStuff

+0

无法为您节省铸造每一个值。 –

+0

您是否希望在转换回来时获得轻微的损失?否则,只需转换一种方式并保留原始文件以保存转换。 –

回答

5

您可以使用SSE此:

float - >double_mm_cvtps_pd

double - >float_mm_cvtpd_ps

首先尝试一个简单的标量循环,但正如(一)编译器可向量化无论如何,(b)你可能会受到内存限制,所以SIMD优化可能没有多大帮助。

+0

这很有用,但它适用于128位XMM寄存器,不是吗?也就是说,每个命令只有两个值。那些使用YMM代替并处理4个值的指令是否有味道? –

+0

是的,当然,请参阅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版本带来任何显着改进。 –

1

数据结构存储为浮点数,但第三方处理库需要double数组。

它可以处理缓存大小的块吗?

如果它没有卡在第三方库中,最好的办法是进行转换,从一对漂浮物中加载一对双打和_mm_cvtps_pd,同样存储回浮标,这样你从来没有在内存中的double阵列。

但是,如果你不能做到这一点,你至少可以将数据反馈到库,同时它还是在L1或L2缓存中的热点阅读一些花车和写一些双打后。实际上,如果它是一个“有线格式”,那么大概数据必须首先通过CPU到内存的路径,除非你有一个零拷贝接收API,DMA直接进入你的缓冲区。收到每个数据包时,转换的理想位置可能会很小。无论是与转换直接复制到double,或复制到两个floatdouble阵列,如果你还需要原始float数据。

+0

库需要一个完整的数组。我没有来源,我阅读并随时转换。仍然,两个为一个的价格:) –

+0

@SevaAlekseyev:是的,无论你可以做什么,以尽量减少你的数据在缓存和RAM之间移动的次数。如果你的数组很大,那么在转换循环中使用NT商店可能是值得的。或者,也许同时写入浮动和双重,使用NT商店为将被使用的第二个?但是,如果一切都适合L3,不要这样做。 –

2

这是不是一个实际的回答你的问题,而只是一个例子如何使转换只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(); 
} 

online example

+1

这是太多的ALU指令值得去做。 (https://godbolt.org/g/1EURvH)。我认为,即使您手动将其从64位变为128位(如打包转换指令),也不值得花费任何周期来使用执行资源来代替FPU转换。 'CVTPS2PD xmm,m64'在Haswell(端口0和一个加载端口)上是2个微处理器,每时钟吞吐量为一个。 ymm,m128版本也一样。因此,Intel CPU可以尽可能快地进行转换(每个时钟一个矢量结果)。 (来源:http://agner.org/optimize/) –

+0

将这个混合到一个加载/转换/存储循环只会降低它在x86上的速度。 –

+0

@PeterCordes也许你是对的,因为它比x64上的正常转换慢2倍,正如我所说这不是一个解决方案,它只是一个例子,你可以做到这一点。我也没有适当的技巧来在汇编级别优化它,但我知道你可以在单核上并行浮动和整数操作,这是我认为他可以使用这种解决方案的基础。 – Logman