JAVA的加法乘法运行时间讨论

前言

在研究计算机底层的数字运算时书本告诉我们:乘法的实现往往靠的是加法的叠加。但是查阅某些资料时又会说CPU的ALU其实是内置了乘法器的但是底层实现好像也需要用到加法器,而且x86和x64的计算机也都提供MUL的汇编操作码,所以这些过于远古的事情真的是非常令人费解也令人着迷。
那么现在就本着一个JAVA程序员的角度去探索这个问题吧。

实验

执行1000000一百万次加法和乘法
888×8代码,三次结果是4939 5256 5025毫秒:

public class mullab {
public static void main(String[] args) {
    long startTime=System.currentTimeMillis();
    System.out.println("开始执行");
    int x=0;
    for(int i=0;i<1000000;i++) {
        x=888*8;
        System.out.println(x);
    }
    long endTime=System.currentTimeMillis();
    System.out.println("程序运行时间: "+(endTime - startTime)+"ms");
}
}

以下代码比较类似就不拿出来了。
8×888代码,三次结果是4927 4947 4947毫秒

888+888....(8次),三次结果是5367 5200 5128毫秒

8+8+8+8+8....(888次,三次结果是4954 5031 4973毫秒

8888×88888,三次结果是5367 5200 5128毫秒
可以见得他们的时间是在一个数量级的,这个问题就出在每次得到结果就进行一次打印输出占用了太多时间(IO时间是远远大于内存执行时间的)。

我以为如果不输出的话优化器就会把中间的for循环给优化成直接执行一次所以转变一下思路,加上System.gc(),强行销毁内存,最后发现gc占用时间也过多,对实验也会造成干扰。

既然for循环可能也有问题,那我就手动插入x+=3;x+=4;这样的代码一万行,人肉for循环,这该总行了吧,结果就是执行时间基本都是0ms,CPU这太强大了,眼看就要卡关了。

就要从JAVA运行的机制找问题了。

JAVA的执行机制

JAVA文件是.java文件是高级语言,计算机自然是看不懂的,所以应该使用javac -encoding utf-8 HelloWorld.java指令进行编译成字节码文件(.class文件也叫CAFEBABE文件),这时候JVM虚拟机就会加载字节码文件,然后使用内部的一些机制去调用计算机底层硬件执行你的程序。

编译与反编译

使用jd-gui反编译工具可以把字节码文件转成JAVA文件进行阅读。
我们发现:
1.之前疯狂插入的一万行x+=3;x+=4;代码是存在的。
2.x=8×88888;就会优化成 i = 711104;编译阶段会把常数进行运算,此语句直接变成赋值运算。
齐活,我们好像要抓住真理了。把x=8×88888改造成x=y×z不就行了吗?

再次实验

执行2147483647次加法和乘法(这次把数字改大点)
1000000×888代码,三次结果是3 3 3毫秒:

public class mullab {
public static void main(String[] args) {
    long startTime=System.currentTimeMillis();
    System.out.println("执行代码块/方法");
    int x=0;
    int y=1000000;
    int z=888;
    for(int i=0;i<2147483647;i++) {
    x=z*y;
    }
    System.out.println(x);
    long endTime=System.currentTimeMillis();
    System.out.println("程序运行时间: "+(endTime - startTime)+"ms");
}
}

x+=1000000×888代码,三次结果是54 53 54毫秒:

public class mullab {
public static void main(String[] args) {
    long startTime=System.currentTimeMillis();
    System.out.println("执行代码块/方法");
    int x=0;
    int y=1000000;
    int z=888;
    for(int i=0;i<2147483647;i++) {
    x+=z*y;
    }
    System.out.println(x);
    long endTime=System.currentTimeMillis();
    System.out.println("程序运行时间: "+(endTime - startTime)+"ms");
}
}

从这里可以看出来JVM还是会去缓存里找数的,for循环的执行不忠诚于程序员的想法。JAVA程序员真是一不小心就把程序写对了呢,想搞砸算法多不容易啊。但是难不倒我呀。
加法执行2147483647次。

int y=1000000;
for(int i=0;i<2147483647;i++) {
    x+=y;
}
执行时间 :56ms
int z=888;
for(int i=0;i<2147483647;i++) {
    x+=z;
}
执行时间 :58ms

乘法执行2147483647次。

int z=888;
for(int i=0;i<2147483647;i++) {
    x*=z;
}
执行时间:1880ms
int y=1000000;
for(int i=0;i<2147483647;i++) {
    x*=y;
}
执行时间: 1852ms

从以上可以看出,确实是乘法远远慢于加法,现在问题是乘法是不是真的是加法的叠加:

  int y=1000000;
  x=2147483647*y;
  执行时间: 1ms
  int y=888;
  x=2147483647*z;
  执行时间: 1ms

这说明用乘法比用加法叠加高效多了。

网上的说法

1.乘法比加法节省,因为对于cpu而言,有专门的 乘法器/移位器 运算单元。

2.x86系列中加法和乘法的执行周期数会和寻道址方式、数据类型等因素有关版系。当寻址方式中需要访问mem时,又会和Cache的命中情况有关。简单的说XX指令执行XX周期是不全准确的。

3.一个指令周期指的是cpu完成一个指令的时间(比如加法指令)cpu周期是指cpu完成一次微指令的时间(微指令就是具体驱动cpu哪些硬件工作的指令,详情请查)而一个微指令耗时1-4个时钟周期不等)当然有些操作如内存存储将耗时多的多,但是cache技术已经很好掩饰了这个问题。做实验的时候(8086)感觉2时钟周期的微指令比较多,而指令一般由2~8个微指令构成,也就是说,一般一个指令周期大概是8个时钟周期。题主说假设cpu主频2.5GHz,那么指令周期就是312.5MHz,再结合之前的论述,不难算出,完成一次乘法大概是6微秒。这还是按照8086这个古董货的技术标准,现代cpu会有更加完美的硬件布线和指令构成,再下降两三个微秒也不是不可能的

结论

尽管资料显示关于计算机底层加法和乘法的速度有许多争论,而且当今从事底层代码分析和硬件设计的人也比较少,资料的获取也比较有限。

但是通过今天的简单分析至少在JAVA编程里应该是奏效的,那就是:
1.乘法的执行时间确实没有加法来得快。
2.但是如果需要用到乘法仍然应当直接写乘法语句,乘法的实现一定不是单单做加法叠加那么简单应该还包含位移和逻辑判断操作。
3.乘法的乘数和被乘数大小位置不影响乘法运算速度。
4.同理有需要进行次方计算也要优先考虑使用MATH函数,JAVA给我们提供了一套比较完善的API体系,过多考虑底层算法是没必要的。
5.本次试验暂时还不能排除FOR循环跳转语句对运算速度的影响,但归根结底可以确认乘法性能非常棒。

因此真正要探讨计算机底层的加法乘法机制就要深入了解汇编语言和操作码以及CPU硬件图纸了。希望未来的某一天能够有机会接触这些东西吧。

附加题(除法)

使用相同的研究方法对除法进行试验研究,发现除法类似,只是除法的执行时间要比乘法还多一个数量级(10倍),具体就不展开了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341