Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.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++ 在单CPU指令中可以在0和1之间翻转位/整数/布尔值的任何可能代码_C++_C_Assembly_X86_Micro Optimization - Fatal编程技术网

C++ 在单CPU指令中可以在0和1之间翻转位/整数/布尔值的任何可能代码

C++ 在单CPU指令中可以在0和1之间翻转位/整数/布尔值的任何可能代码,c++,c,assembly,x86,micro-optimization,C++,C,Assembly,X86,Micro Optimization,单个x86指令能否在“0”和“1”之间切换布尔值 我想到了下面的方法,但所有的结果都是两条指令带有gcc的-O3标志 status =! status; status = 1 - status; status = status == 0 ? 1: 0; int flip[2] = {1, 0}; status = flip[status]; 有没有更快的方法 这就是我所尝试的: 我需要的是一个切换输入和返回的函数,以编译成一条指令的方式编写。类似于此函数的内容: int addOne

单个x86指令能否在“0”和“1”之间切换布尔值

我想到了下面的方法,但所有的结果都是两条指令带有gcc的-O3标志

status =! status;

status = 1 - status;

status  = status == 0 ? 1: 0;

int flip[2] = {1, 0};
status = flip[status];
有没有更快的方法

这就是我所尝试的:


我需要的是一个切换输入和返回的函数,以编译成一条指令的方式编写。类似于此函数的内容:

int addOne(int n) { return n+1; }
为此:

  lea eax, [rdi+1]    # return n+1 in a single instruction
  ret
由此看来(这段代码基本上包含了我尝试过的几个选项),XORing似乎给出了一个可以做到这一点的语句:-(正如您所说的,切换就是您要寻找的)

归结为的单个指令(这是使用
-O0

使用
-O3
可以看到您提到的所有方法都使用
xor
,特别是
mov-eax、edi/xor-eax、1

这确保了状态从
0
切换到
1
,反之亦然。(因为存在
xor
语句-这在大多数体系结构中都存在,并且在许多情况下很有用)

我让内存访问的另一个选项失败了,因为指针算法和对地址的去引用不会比这些(可能的内存访问)更快

我已经提出了一种基于戈德博尔特的小混乱的方法。从这里你可以做的是——比较不同的方法,然后得到你得到的时间结果。据推测,您将得到
XOR
-ing的结果在您的机器架构上不会那么糟糕

有趣的是,正如彼得·科尔德(Peter Cordes)在这个例子中所说,这对布尔人也适用


有了这一点,很明显,编译器优化到了
1
版本的未优化代码的xoring。这是一种支持xoring在正常int操作的情况下会产生更好结果的方法。当使用
-O3
编译布尔值时,上面显示的所有布尔值都会流入
mov eax、edi/xor eax、1
要在整数中翻转一位,请使用
xor
如下所示:
foo^=1

gcc已经知道这个优化是针对
bool
,所以你可以
返回!地位像普通人一样,不损失任何效率。gcc也将
status^=1
编译为xor指令。事实上,除了查表之外,您所有的想法都会编译成一条带有
bool
输入/返回值的
xor
指令

使用
gcc-O3
查看它,asm输出窗格用于
bool
int

MYTYPE func4(MYTYPE status) {
    status ^=1;
    return status;
}

  # same code for bool or int
  mov eax, edi
  xor eax, 1
  ret
vs


为什么
bool
int
不同? 要求传递
bool
的调用方传递0或1值,而不仅仅是任何非零整数。因此,编译器可以假设输入是这样的

但是对于
intfoo
,C表达式
!foo
要求对值进行“布尔化”<代码>!foo
具有类型
\u Bool
/(如果您
\include
,则称为
Bool
),将其转换回整数必须产生0或1的值。如果编译器不知道
foo
必须是
0
1
,则无法优化
!foo
foo^=1
,并且无法意识到
foo^=1
在truthy/falsy之间翻转一个值。(在C中,
if(foo)
表示
if(foo!=0)

这就是为什么您会得到test/setcc(在
test
之前将零扩展为32位
int

相关:。像
(bool1&bool2)这样的东西?x:y
并非总是像您希望的那样高效编译。编译器相当不错,但确实遗漏了优化bug


那额外的
mov
指令呢? 如果编译器不需要/不想在以后保留旧的未翻转值,则在内联时它将消失。但在独立函数中,第一个参数位于
edi
,返回值需要位于
eax
(在x86-64 System V调用约定中)

像这样的小函数与作为大函数的一部分可能得到的结果非常接近(如果此翻转无法优化为其他内容),但需要在不同寄存器中得到结果是一个混淆因素


x86没有复制和异或整数指令,因此对于独立函数,至少需要一个
mov
才能从arg传递寄存器复制到
eax

lea
是一种特殊的
:它是为数不多的整数ALU指令之一,可以将结果写入另一个寄存器,而不是破坏其输入
lea
是一个,但x86中没有复制和异或指令。许多RISC指令集都有3个操作数的指令,例如MIPS可以执行异或$t1、$t2、$t3


AVX引入了矢量指令的非破坏性版本(在大量代码中节省了大量的
movdqa
/
movups
寄存器复制),但对于integer,只有少数新指令执行不同的操作。例如,
eax=rotate_right(ecx,16)
,并使用与非破坏性AVX指令相同的VEX编码。

如果您试图微优化布尔运算,您要么过早优化,要么对大量布尔数据执行大量运算。对于前者,答案是不要;对于后者,你可能问错了问题。如果真正的问题是如何优化(许多)布尔数据上的(许多)操作,那么答案是使用基于“标志”的替代表示法(也称为使用更好的算法)。这将允许您以可移植且可读的方式将更多数据放入缓存,并同时执行多个操作和测试
xor DWORD PTR [rbp-4], 1
MYTYPE func4(MYTYPE status) {
    status ^=1;
    return status;
}

  # same code for bool or int
  mov eax, edi
  xor eax, 1
  ret
MYTYPE func1(MYTYPE status) {
    status = !status;
    return status;
}

  # with -DMYTYPE=bool
  mov eax, edi
  xor eax, 1
  ret

  # with int
  xor eax, eax
  test edi, edi
  sete al
  ret
int isconsonant(int c){
    const unsigned consonant_mask = (1<<('b'-'a'))|
    (1<<('c'-'a'))|(1<<('d'-'a'))|(1<<('f'-'a'))|(1<<('g'-'a'))|
    (1<<('h'-'a'))|(1<<('j'-'a'))|(1<<('k'-'a'))|(1<<('l'-'a'))|
    (1<<('m'-'a'))|(1<<('n'-'a'))|(1<<('p'-'a'))|(1<<('q'-'a'))|
    (1<<('r'-'a'))|(1<<('s'-'a'))|(1<<('t'-'a'))|(1<<('v'-'a'))|
    (1<<('w'-'a'))|(1<<('x'-'a'))|(1<<('y'-'a'))|(1<<('z'-'a'));
    unsigned x = (c|32)-'a'; // ~ tolower
    /* if 1<<x is in range of int32 set mask to position relative to `a`
     * as in the mask above otherwise it is set to 0 */
    int ret = (x<32)<<(x&31);
    return ret & consonant_mask;
}
//compiles to 7 operations to check for 52 different values
isconsonant:
  or edi, 32 # tmp95,
  xor eax, eax # tmp97
  lea ecx, [rdi-97] # x,
  cmp ecx, 31 # x,
  setbe al #, tmp97
  sal eax, cl # ret, x
  and eax, 66043630 # tmp96,
  ret
//inline these if your compiler doesn't automatically
_Bool isSpecificMaskSet(uint32_t x, uint32_t m){
    return x==m; //returns 1 if all bits in m are exactly the same as x
}

_Bool isLimitedMaskSet(uint32_t x, uint32_t m, uint32_t v){
    return (x&m) == v;
    //returns 1 if all bits set in v are set in x
    //bits not set in m are ignored
}

_Bool isNoMaskBitSet(uint32_t x, uint32_t m){
    return (x&m) == 0; //returns 1 if no bits set in m are set in x
}

_Bool areAllMaskBitsSet(uint32_t x, uint32_t m){
    return (x&m) == m; //returns 1 if all bits set in m are set in x
}

uint32_t setMaskBits(uint32_t x, uint32_t m){
    return x|m; //returns x with mask bits set in m
}

uint32_t toggleMaskBits(uint32_t x, uint32_t m){
    return x^m; //returns x with the bits in m toggled
}

uint32_t clearMaskBits(uint32_t x, uint32_t m){
    return x&~m; //returns x with all bits set in m cleared
}

uint32_t getMaskBits(uint32_t x, uint32_t m){
    return x&m; //returns mask bits set in x
}

uint32_t getMaskBitsNotSet(uint32_t x, uint32_t m){
    return (x&m)^m; //returns mask bits not set in x
}