php 右移位,什么是位移(位移)操作符,它们是如何工作的?

假设我们有一个字节:

0110110

应用一个左移位让我们:

1101100

最左边的零被移出该字节,并且在该字节的右端追​​加了一个新的零。

位不翻转; 他们被丢弃。 这意味着,如果您左移1101100,然后右移,则不会得到相同的结果。

左移N等于乘以2 N。

向右移动N(如果使用的是补码 )等于除以2 N并舍入为零。

偏移可以用于疯狂的快速乘法和除法,只要你正在使用2的幂。几乎所有的低级graphics例程使用偏移。

例如,在过去的时代,我们使用模式13h(320×200 256色)进行游戏。 在模式13h中,video存储器按照每个像素顺序排列。 这意味着要计算一个像素的位置,你可以使用下面的math公式:

memoryOffset = (row * 320) + column

现在,在那个时代,速度是至关重要的,所以我们会用位移来做这个操作。

然而,320并不是二的力量,所以为了解决这个问题,我们必须找出两个加在一起的力量是320:

(row * 320) = (row * 256) + (row * 64)

现在我们可以把它转换成左移:

(row * 320) = (row << 8) + (row << 6)

最终结果如下:

memoryOffset = ((row << 8) + (row << 6)) + column

现在我们得到和以前相同的偏移量,除了代替昂贵的乘法运算,我们使用了两个位移…在x86中,它会是这样的(注意,从我做完程序集到现在一直是这样(编者注:校正几个错误,并添加了一个32位的例子)):

mov ax, 320; 2 cycles mul word [row]; 22 CPU Cycles mov di,ax; 2 cycles add di, [column]; 2 cycles ; di = [row]*320 + [column] ; 16-bit addressing mode limitations: ; [di] is a valid addressing mode, but [ax] isn't, otherwise we could skip the last mov

总计:在任何古代CPU有这些时间的28个周期。

VRS

mov ax, [row]; 2 cycles mov di, ax; 2 shl ax, 6; 2 shl di, 8; 2 add di, ax; 2 (320 = 256+64) add di, [column]; 2 ; di = [row]*(256+64) + [column]

在同一个古老的CPU上有12个周期。

是的,我们将努力削减16个CPU周期。

在32位或64位模式下,两个版本都变得更短更快。 像Intel Skylake(参见http://agner.org/optimize/ )这样的现代无序执行CPU具有非常快的硬件乘法(低延迟和高吞吐量),所以增益要小得多。 AMD推土机系列有点慢,特别是对于64位的乘法。 在Intel CPU和AMD Ryzen上,两次转换的延迟稍微低一些,但比乘法的指令要多(这可能会导致较低的吞吐量):

imul edi, [row], 320 ; 3 cycle latency from [row] being ready add edi, [column] ; 1 cycle latency (from [column] and edi being ready). ; edi = [row]*(256+64) + [column], in 4 cycles from [row] being ready.

mov edi, [row] shl edi, 6 ; row*64. 1 cycle latency lea edi, [edi + edi*4] ; row*(64 + 64*4). 1 cycle latency add edi, [column] ; 1 cycle latency from edi and [column] both being ready ; edi = [row]*(256+64) + [column], in 3 cycles from [row] being ready.

编译器会为你做这件事情:看优化return 320*row + col;时,gcc,clang和MSVC如何使用shift + lea return 320*row + col; 。

这里最值得注意的是x86有一个移位和加法指令( LEA ) ,它可以执行小的左移,同时添加性能和add指令。 ARMfunction更强大:任何指令的一个操作数可以左右移位。 因此,通过一个已知是2的乘方的编译时常量进行缩放可能比乘法更有效。

好吧,回到现代…现在更有用的东西就是使用位移来存储16位整数中的两个8位值。 例如,在C#中:

// Byte1: 11110000 // Byte2: 00001111 Int16 value = ((byte)(Byte1 >> 8) | Byte2)); // value = 000011111110000;

在C ++中,编译器应该为你做这个,如果你使用两个8位成员的结构,但实际上并不总是如此。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值