Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C+中的有效整数层函数+;_C++_Performance_X86 64_Processing Efficiency_Floor - Fatal编程技术网

C++ C+中的有效整数层函数+;

C++ C+中的有效整数层函数+;,c++,performance,x86-64,processing-efficiency,floor,C++,Performance,X86 64,Processing Efficiency,Floor,我想定义一个有效的整数楼层函数,即从float或double到负无穷大执行截断的转换 我们可以假设这些值不会发生整数溢出。到目前为止,我有几个选择 铸造到int;这需要对负值进行特殊处理,因为强制转换将向零截断 I= int(F); if (I < 0 && I != F) I--; 通过大移位将其转换为int以获得正值(对于大值,这可能返回错误的结果) 转换为int是出了名的慢。if测试也是如此。我还没有为楼层功能计时,但看到一些帖子声称它也很慢 在速度、准确度

我想定义一个有效的整数楼层函数,即从float或double到负无穷大执行截断的转换

我们可以假设这些值不会发生整数溢出。到目前为止,我有几个选择

  • 铸造到int;这需要对负值进行特殊处理,因为强制转换将向零截断

    I= int(F); if (I < 0 && I != F) I--;
    
  • 通过大移位将其转换为int以获得正值(对于大值,这可能返回错误的结果)

转换为int是出了名的慢。if测试也是如此。我还没有为楼层功能计时,但看到一些帖子声称它也很慢

在速度、准确度或允许的射程方面,你能想出更好的选择吗?它不需要便携。目标是最新的x86/x64体系结构。

看看。在网页上提出的算法应该比简单的强制转换更有效。我自己从未使用过它,但这是他们在网站上提供的性能比较(xs_ToInt和xs_CRoundToInt是建议的功能):

此外,xs_-ToInt显然进行了修改,以提高性能:

Performing 10000000 times:
simple cast convert   3186 ms i.e. fi = (f*65536);
fistp convert         3031 ms i.e. fi = FISTToInt(f*65536);
xs_ToFix               622 ms i.e. fi = xs_Fix<16>::ToFix(f);
执行10000000次:
简单转换3186毫秒,即fi=(f*65536);
fistp转换3031毫秒,即fi=FISTINT(f*65536);
xs_-ToFix 622 ms,即fi=xs_-Fix::ToFix(f);
简要说明“幻数”方法的工作原理:

“基本上,为了添加两个浮点数,处理器将数字的小数点“排列”起来,这样就可以轻松地添加位。它通过“规范化”数字来实现这一点,以便保留最重要的位,即较小的数字“规范化”以匹配较大的数字。因此,“幻数”的原理xs_CRoundToInt()使用的转换是这样的:我们将一个足够大的浮点数(一个大到只有小数点以下才有有效数字的数字,后面没有有效数字)添加到要转换的浮点数上,以便:(a)处理器将该数字标准化为其等效整数;(b)将这两个数字相加不会擦除您试图转换的数字中的整数有效位(即XX00+00YY=XXYY)。”


引用取自同一网页。

如果您批量执行此操作,编译器可能会自动矢量化它,如果您知道您正在执行的操作。例如,下面是一个小型实现,它在GCC上自动矢量化浮点到整数的转换:

#include <cmath>

// Compile with -O3 and -march=native to see autovectorization
__attribute__((optimize("-fno-trapping-math")))
void testFunction(float* input, int* output, int length) {
  // Assume the input and output are aligned on a 32-bit boundary.
  // Of course, you have  to ensure this when calling testFunction, or else
  // you will have problems.
  input = static_cast<float*>(__builtin_assume_aligned(input, 32));
  output = static_cast<int*>(__builtin_assume_aligned(output, 32));

  // Also assume the length is a multiple of 32.
  if (length & 31) __builtin_unreachable();

  // Do the conversion
  for (int i = 0; i < length; ++i) {
    output[i] = floor(input[i]);
  }
}
如果您的目标不支持AVX512,它仍将使用SSE4.1指令(假设您有这些指令)进行自动矢量化。这是带有
-O3-msse4.1
的输出:

testFunction(float*,int*,int):
测试edx,edx
jle.L1
shr-edx,2
异或eax,eax
sal rdx,4岁
.L3:
roundps xmm0,xmmwordptr[rdi+rax],1
cvttps2dq xmm0,xmm0
movaps XMMWORD PTR[rsi+rax],xmm0
加上rax,16
cmp-rax,rdx
jne.L3
.L1:
ret
转换为int是出了名的慢

也许您从x86-64开始就一直生活在一块岩石下,或者在x86上错过了这一事实

SSE/SSE2有一条使用截断进行转换的指令(而不是默认舍入模式)。ISA有效地支持此操作,正是因为在实际代码库中使用C语义的转换并不少见。x86-64代码使用SSE/SSE2 XMM寄存器进行标量FP数学运算,而不是x87,因为这一点和其他一些因素使其更为有效。即使是现代32位代码也使用XMM寄存器进行标量数学运算

在为x87编译时(没有SSE3
fisttp
),编译器过去必须将x87舍入模式更改为截断,将FP存储更改为内存,然后再次更改舍入模式。(然后从内存重新加载整数,通常是从堆栈上的本地重新加载整数,如果对其进行进一步操作的话。)x87对此非常糟糕

是的,速度非常慢,例如在2006年,当@Kirjain的答案中的链接被写入时,如果您仍然有一个32位CPU,或者正在使用x86-64 CPU运行32位代码


不直接支持使用截断或默认(最接近)以外的舍入模式进行转换,并且直到SSE4.1
roundps
/
roundpd
您最好的选择是像@Kirjain的答案中的数字魔术

这里有一些不错的技巧,但只适用于
double
->32位整数。如果您有
float
,则不太值得扩展到
double

或者更常见的情况是,简单地添加一个较大的数值以触发舍入,然后再减去它以返回到原始范围。这可以用于
浮点
,而不会扩展到
双精度
,但我不确定让
下限
工作有多容易


无论如何,这里最明显的解决方案是
\u mm256\u floor\u ps()
\u mm256\u cvtps\u epi32
vroundps
vcvtps2dq
)。非AVX版本可以与SSE4.1一起使用。

我不确定我们是否能做得更好;如果您有一个庞大的数组要处理(并且无法将此工作与其他工作交叉进行),您可以将MXCSR舍入模式设置为“朝向-Inf”(地板),只需使用
vcvtps2dq
(使用当前舍入模式)。然后将其设置回原位。但最好是缓存阻止转换,或者在生成数据时动态执行转换,可能是从需要将FP舍入模式设置为默认最接近模式的其他FP计算中生成的

roundps
/pd/ss/sd在英特尔CPU上是2个uop,但在AMD Ryzen上只有1个uop(每128位通道)。
cvtps2dq
也是1个uop。压缩双精度->整数转换还包括一个随机数。标量FP->整数转换(复制到整数寄存器)通常也需要额外的uop

这就是roo
Performing 10000000 times:
simple cast           2819 ms i.e. i = (long)f;
xs_ToInt              1242 ms i.e. i = xs_ToInt(f); //numerically same as above
bit-twiddle(full)     1093 ms i.e. i = BitConvertToInt(f); //rounding from Fluid
fistp                  676 ms i.e. i = FISTToInt(f); //Herf, et al x86 Assembly rounding 
bit-twiddle(limited)   623 ms i.e. i = FloatTo23Bits(f); //Herf, rounding only in the range (0...1]  
xs_CRoundToInt         609 ms i.e. i = xs_CRoundToInt(f); //rounding with "magic" numbers
Performing 10000000 times:
simple cast convert   3186 ms i.e. fi = (f*65536);
fistp convert         3031 ms i.e. fi = FISTToInt(f*65536);
xs_ToFix               622 ms i.e. fi = xs_Fix<16>::ToFix(f);
#include <cmath>

// Compile with -O3 and -march=native to see autovectorization
__attribute__((optimize("-fno-trapping-math")))
void testFunction(float* input, int* output, int length) {
  // Assume the input and output are aligned on a 32-bit boundary.
  // Of course, you have  to ensure this when calling testFunction, or else
  // you will have problems.
  input = static_cast<float*>(__builtin_assume_aligned(input, 32));
  output = static_cast<int*>(__builtin_assume_aligned(output, 32));

  // Also assume the length is a multiple of 32.
  if (length & 31) __builtin_unreachable();

  // Do the conversion
  for (int i = 0; i < length; ++i) {
    output[i] = floor(input[i]);
  }
}
#include <cmath>

auto floor_(float const x) noexcept
{
  int const t(x);

  return t - (t > x);
}
#include <assert.h>
#include <cmath>
#include <stddef.h>
#include <stdint.h>

#define ALIGNMENT alignof(max_align_t)
using std::floor;

// Compiled with: -std=c++17 -Wall -Wextra -Wpedantic -Wconversion -fno-trapping-math -O -march=cannonlake -mprefer-vector-width=512

void testFunction(const float in[], int32_t out[], const ptrdiff_t length)
{
  static_assert(sizeof(float) == sizeof(int32_t), "");
  assert((uintptr_t)(void*)in % ALIGNMENT == 0);
  assert((uintptr_t)(void*)out % ALIGNMENT == 0);
  assert((size_t)length % (ALIGNMENT/sizeof(int32_t)) == 0);

  alignas(ALIGNMENT) const float* const input = in;
  alignas(ALIGNMENT) int32_t* const output = out;

  // Do the conversion
  for (int i = 0; i < length; ++i) {
    output[i] = static_cast<int32_t>(floor(input[i]));
  }
}
.L7:
        vrndscaleps     zmm0, ZMMWORD PTR [rdi+rax], 1
        vcvttps2dq      zmm0, zmm0
        vmovdqu32       ZMMWORD PTR [rsi+rax], zmm0
        add     rax, 64
        cmp     rax, rcx
        jne     .L7
        test    rdx, rdx
        jle     .LBB0_14
        cmp     rdx, 63
        ja      .LBB0_6
        xor     eax, eax
        jmp     .LBB0_13
.LBB0_6:
        mov     rax, rdx
        and     rax, -64
        lea     r9, [rax - 64]
        mov     r10, r9
        shr     r10, 6
        add     r10, 1
        mov     r8d, r10d
        and     r8d, 1
        test    r9, r9
        je      .LBB0_7
        mov     ecx, 1
        sub     rcx, r10
        lea     r9, [r8 + rcx]
        add     r9, -1
        xor     ecx, ecx
.LBB0_9:                                # =>This Inner Loop Header: Depth=1
        vrndscaleps     zmm0, zmmword ptr [rdi + 4*rcx], 9
        vrndscaleps     zmm1, zmmword ptr [rdi + 4*rcx + 64], 9
        vrndscaleps     zmm2, zmmword ptr [rdi + 4*rcx + 128], 9
        vrndscaleps     zmm3, zmmword ptr [rdi + 4*rcx + 192], 9
        vcvttps2dq      zmm0, zmm0
        vcvttps2dq      zmm1, zmm1
        vcvttps2dq      zmm2, zmm2
        vmovups zmmword ptr [rsi + 4*rcx], zmm0
        vmovups zmmword ptr [rsi + 4*rcx + 64], zmm1
        vmovups zmmword ptr [rsi + 4*rcx + 128], zmm2
        vcvttps2dq      zmm0, zmm3
        vmovups zmmword ptr [rsi + 4*rcx + 192], zmm0
        vrndscaleps     zmm0, zmmword ptr [rdi + 4*rcx + 256], 9
        vrndscaleps     zmm1, zmmword ptr [rdi + 4*rcx + 320], 9
        vrndscaleps     zmm2, zmmword ptr [rdi + 4*rcx + 384], 9
        vrndscaleps     zmm3, zmmword ptr [rdi + 4*rcx + 448], 9
        vcvttps2dq      zmm0, zmm0
        vcvttps2dq      zmm1, zmm1
        vcvttps2dq      zmm2, zmm2
        vcvttps2dq      zmm3, zmm3
        vmovups zmmword ptr [rsi + 4*rcx + 256], zmm0
        vmovups zmmword ptr [rsi + 4*rcx + 320], zmm1
        vmovups zmmword ptr [rsi + 4*rcx + 384], zmm2
        vmovups zmmword ptr [rsi + 4*rcx + 448], zmm3
        sub     rcx, -128
        add     r9, 2
        jne     .LBB0_9
        test    r8, r8
        je      .LBB0_12
.LBB0_11:
        vrndscaleps     zmm0, zmmword ptr [rdi + 4*rcx], 9
        vrndscaleps     zmm1, zmmword ptr [rdi + 4*rcx + 64], 9
        vrndscaleps     zmm2, zmmword ptr [rdi + 4*rcx + 128], 9
        vrndscaleps     zmm3, zmmword ptr [rdi + 4*rcx + 192], 9
        vcvttps2dq      zmm0, zmm0
        vcvttps2dq      zmm1, zmm1
        vcvttps2dq      zmm2, zmm2
        vcvttps2dq      zmm3, zmm3
        vmovups zmmword ptr [rsi + 4*rcx], zmm0
        vmovups zmmword ptr [rsi + 4*rcx + 64], zmm1
        vmovups zmmword ptr [rsi + 4*rcx + 128], zmm2
        vmovups zmmword ptr [rsi + 4*rcx + 192], zmm3
.LBB0_12:
        cmp     rax, rdx
        je      .LBB0_14
.LBB0_13:                               # =>This Inner Loop Header: Depth=1
        vmovss  xmm0, dword ptr [rdi + 4*rax] # xmm0 = mem[0],zero,zero,zero
        vroundss        xmm0, xmm0, xmm0, 9
        vcvttss2si      ecx, xmm0
        mov     dword ptr [rsi + 4*rax], ecx
        add     rax, 1
        cmp     rdx, rax
        jne     .LBB0_13
.LBB0_14:
        pop     rax
        vzeroupper
        ret
.LBB0_7:
        xor     ecx, ecx
        test    r8, r8
        jne     .LBB0_11
        jmp     .LBB0_12