拾谈“用最有效率的方法算出2乘以8等於几?”

原创 2006年06月18日 15:11:00

这是网上流传的“变态级JAVA程序员面试32问”的其中一题(二十八题),然后下面给出来的答案是

第二十八,编程题: 用最有效率的方法算出2乘以8等於几?
  有C背景的程序员特别喜欢问这种问题。
  
  2 << 3

粗看似乎很在理,大致想来2<<3会是移位操作,在Java的字节码中移位指令是ishl(右移),而在CPU上的硬件指令可能就会是shl(算术右移指令)。其实不然,如果熟悉汇编语言,还考虑过编译优化,2<<3根本不会使用移位操作,而是在编译时就优化计算出16来了。

但如果是写成这样的方式(int i = 2; int j = i<<2;),又是不一样的,大家可以从代码不同写法生成的Java操作指令或汇编指令看出个端倪。 

Java代码 对应的字节码指令 说明
int i = 2 << 3; 0:   bipush  16 编译时就把2左移3位的置算出来了,可以说降低了编译时效率
栈中弹出int型值,存到位置为1的局部变量中
int i = 2 * 8; 与上同
int i = 2;
int j = i * 8;
0:   iconst_2
1:   istore_1
2:   iload_1
3:   iconst_3
4:   ishl 
6:   istore_2
常量2压栈
栈中弹出int型值,存到位置为1的局部变量中
位置为1的int型局部变量压栈
常量3压栈
左移位操作,结果值压栈
栈中弹出int型值,存到位置为2的局部变量中
int i = 2;
int j = i * 8;
0:   iconst_2
1:   istore_1
2:   iload_1
3:   bipush  8
5:   imul 
6:   istore_2
javac 生成的字节码操作指令是 imul,javac未作优化

所以把代码写成 int i = 2 << 3,和写成 int i = 16; 是一样的,前者还有降低了编译时效率

下面再举例说明一下,VC++编译器,其实也会作这样的优化

C++ 代码 对应的汇编指令 说明
int i = 2 << 3; mov dword ptr [ebp-4],10h 也是把2<<3算出来了,为16(10h),放入内存
int i = 2 * 8; 与上同
int i = 2;
int j = i << 3;
mov     dword ptr [ebp-4],2
mov     eax,dword ptr [ebp-4]
shl     eax,3 
mov     dword ptr [ebp-8],eax
把2放入内存中 SS:[ebp-4]
把 SS:[ebp-4] 整数传送到eax寄存器中
 对eax中的数据左移3位
再把移位后的结果存到内存 SS:[ebp-8]中
int i = 2;
int j =  i * 8
与上同 VC++自动优化成移位操作,因为8为2的整数次幂
int i = 2;
int j= i * 9;
mov      dword ptr [ebp-4],2
mov      eax,dword ptr [ebp-4]
imul     eax,eax,9 
mov      dword ptr [ebp-8],eax
生成的汇编指令为 imul ,因为9不是2的整数次幂

所以从回答说是 int i = 2 << 3; 并没改变一点执行上的效率,因为它 int i = 16; 是完全一样的。但如果写成

int i = 2;
int j = i << 3;

比写成

int i = 2;
int j = i * 8:

确实会提高一定的执行效率, 因为 i * 8,要用Java操作指令 imul 进行运算,这也只是说JDK的编译器javac是这么处理的。JDK编译时并没有 i * 8 优化成 i << 3 。

如果把代码换成是C++代码,也是写成

int i = 2;
int j = i * 8:

由VC++编译出来的汇编指令代码也还是用 shl eax, 3,它和写成 int j = i << 3 全一致的。VC++从效率出发已经给你作了这样的代优化,再回味一下这个问题的答案中的一句话“有C背景的程序员特别喜欢问这种问题。”,可以获知那个有C背景的程序员在这里耍了一个“小聪明”,却又没有占到一点小便宜,只因为他低估了编译器的优化功能。

之所以问这样问题的人,他们只是基于这样一个事实,整数乘法或整数除法所需要的时钟周期远远大于移位操作所需的时钟周期,下面列出这个指令的基本执行时间:

移位指令             寄存器移 1 位        时钟周期数为 2 
整数乘法 IMUL        16 位寄存器乘        时钟周期为 128 ~ 154
整数除法 IDIV        16 位寄存器          时钟周期为 165 ~ 184

 而即使 Java编译器在编译 int j = i * 8; 时用的是   imul ,但真正执行这这段代码,由虚拟机JVM转换成本地代码是时候会不会进一步优化成用移位操作的汇编指也未得而知,必要时当然可追踪一下java.exe的执行过程,即使执行时会作此优化,在java中把 int j = i * 8 写成 int j = i <<3 ,可获取一点点的效率,微不足道。

任外,在Visual C++ .net 2003,还增加了对Intel Pentium 4 和 AMD Athlon的代码优化,当用 /G7 编译时,可以产生了更快(但更长)的指令序列,避免了使用 imul 指令,该指令在 Intel Pentium 4 上具有 14 个周期的滞后时间。

如 i * 9,可能被编译成如下代码
mov ecx, dword ptr _i$[esp-4]
mov eax, ecx
shl eax, 3
add eax, ecx

本来应该是 imul 乘法指令,用/G7编译选项巧妙的生成了先左移3位,再加上原来的值。网上介绍的是这么说的,可以我在Visual C++ .net 2003,用/G7选项编译时却没有生成与上类似的汇编代码,仍然是生成的 imul 指令。

关于用Visual C++ 优化代码有一篇文章可以参考 [http://www1.softhouse.com.cn/html/200408/2004083110013100000450.html]

最后再从业务出发,比如说这样一个简单需求,A的工资是B的工资的8倍(B可是个低级奴隶了),这种情况下要你写代码是写成

salaryA = salaryB * 8;

还是写成

salaryA = salaryB << 3;

这种问题不用回答了吧。

如果说用 int j = i <<3 的形式的情况有没有呢,也有,一般是在处理字节的时候,比如所有位整体左移3位,低3位再用字节填充,但也比较少牵涉到 8 倍这个概念。

我昨日就到一个公司面试,看了一些面试题(包括系统分析题),真不太想做,我那时候就跟他们说过:我一直以来就不太喜欢做题,做题的时候可能常常不是关注问题本身,还得留个意去揣摩出题人的用意。

象中国人的英语考试中的选择题那样,如果四个答案是分别是

A: aab    B: aac    C: aad    D: 456

一般老师会告诉你用排除法排除答案D,因为D太不相似了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

用最有效率的方法算出2乘以8等於几?

用最有效率的方法算出2乘以8等於几? 2的二进制表示法左移3位,2的二进制应该是0000 0010,左移3位后0001 0000(可以看成是一个框就能显示8位数据,向左移动后前面的三位不...

Java中用最有效率的方法算出2 乘以8

Java中用最有效率的方法算出2 乘以8 等於几? 2 因为将一个数左移n 位,就相当于乘以了2 的n 次方,那么,一个数乘以8 只要将其左移3 位 即可,而位运算cpu 直接支持的,效率最高...

java面试题之用最有效率的方法算出2乘以8等於几

这是网上流传的"变态级JAVA程序员面试32问"的其中一题(二十八题),然后下面给出来的答案是第二十八,编程题: 用最有效率的方法算出2乘以8等於几?  有C背景的程序员特别喜欢问这种问题。    2...

请设计一个一百亿的计算器

首先要明白这道题目的考查点是什么, 一是大家首先要对计算机原理的底层细节要清楚、要知道加减法的位运算原理和知道计算机中的算术运算会发生越界的情况, 二是要具备一定的面向对象的设计思想。 首先,计...

用最有效率的方法算出2乘以8等於几?

最有效的方法是: public class Demo02 {          public static void main(String args[]){     ...

java面试题之用最有效率的方法算出2乘以8等於几

这是网上流传的"变态级JAVA程序员面试32问"的其中一题(二十八题),然后下面给出来的答案是 第二十八,编程题: 用最有效率的方法算出2乘以8等於几?   有C背景的程序员特别喜欢问这种问题。   ...

用最有效率的方法算出2乘以8等于几?(C#移位运算)

出处:http://www.cnblogs.com/flydoos/archive/2011/09/06/2169280.html C#移位运算(左移和右移)   这是网上流传的“变态级JA...

UVA - 1354(指针结点的联合方法 || 高效算法复杂度未算出)

指针结点联合,即每个指针指向一个实际node,

2N点实数序列为 N=64。用一个复数FFT程序,一次算出,并绘出。

2N点实数序列为 N=64。用一个复数FFT程序,一次算出,并绘出。 试利用MATLAB编程完成计算,绘出相应图形。并与理论计算相比较,说明实验结果的原因。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)