计组对于学计算机重要吗,学习计算机组成原理有什么用,意义何在?

学习计算机组成原理有什么用?

从StackOverFlow上一个很经典的问题说起

运行下面的程序,然后比较加上Arrays.sort(data)这行之后,程序速度的变化

import java.util.Arrays;

import java.util.Random;

public class Test

{

public static void main(String[] args)

{

// Generate data

int arraySize = 32768;

int data[] = new int[arraySize];

Random rnd = new Random(0);

for (int c = 0; c < arraySize; ++c)

data[c] = rnd.nextInt() % 256;

// !!! With this, the next loop runs faster

//Arrays.sort(data);

// Test

long start = System.nanoTime();

long sum = 0;

for (int i = 0; i < 100000; ++i)

{

// Primary loop

for (int c = 0; c < arraySize; ++c)

{

if(data[c]>128)

sum+=data[c];

}

}

System.out.println((System.nanoTime() - start) / 1000000000.0);

System.out.println("sum = " + sum);

}

}

在我的电脑上,对数组排序后程序运行速度是原来的四倍,这就奇了怪了,怎么排个序就快了这么多?你可能模模糊糊知道应该往计组的方面想,要从底层找原因,毕竟这篇博文就是试图向你说明了解计组有什么用。

现在将排序注释掉,并把primary loop里面的代码换成

int t = (data[c] - 128) >> 31;

sum += ~t & data[c];

再次运行程序,你会发现程序运行的时间跟排序后的运行时间差不多,甚至还快一点。有意思吧,通过前面的两次改动,我们已经把问题锁定在了Primary loop这里,现在看来是if语句搞的鬼。没想到这个平日里看起来浓眉大眼,人畜无害的东西也会给我玩花样,想不到想不到。

为了快点写完作业,我选择瞎蒙

现代计算机的设计,在提高运行速度这块想出了很多办法,cache绝对算的上是最精彩的那一个。不过跟本问题相关的是流水线的设计,我们还是先来认识这熟悉又陌生的词语吧。

流水线是一种实现多条指令重叠执行的技术,你可以类比于生产流水线。

1ab3d0f65ea5f72e737646b41599b65e.png

类似于这样,大家印象中的流水线都是高速运转的,没有人希望它停顿。如果流水线存在分支的话,按照常理来讲,我们只能是停下来等结果。等比较的结果出来了,再决定走哪条分支 。流水线说,我是一个永不止步的男人,怎么能因为这点事就停顿呢?忍不了,咋整呢?

有一种办法是分支冒险(branch hazard),虽然名字听起来很高大上,其实就是赌一把,蒙一个。我假设这个分支发生,赌对了自然什么都不用做,赌错了就得丢弃假设不发生之后执行的指令。这有点像赶作业的时候,碰到判断题如果你不想做的话,就一路打×,因为这种题目大概率打叉的情况多。当然,计算机没有你那么聪明不知道这条规律,但它可以使用动态分支预测的方法来提高预测的准确性。

这里介绍两位预测位机制,猜错两次就变换猜测方向。

1b2a5c7323454fb498da5eb88216451d.png

让我们再来回忆一下之前的实例程序的流程,在0-256的范围随机生成了一个数组,并且将大于128的值加到sum变量中。现在,让我们用刚刚学到的知识分析那条万恶的if语句

if(data[c]>128)

sum+=data[c];

累计两次预测失败我们就会变换预测方向,如果不对数组进行排序的话,数组中的数字是随机分布的,我们预测的准确性将会非常低。这意味着我们将会耗费大量的时间在:将流水线中投机执行的中间结果全部抛弃,重新获取正确分支路线上的指令执行。如果是已经排好序的数组,最坏的情况下我们也只有四次预测失败,这为我们节省了大把的时间。这也是为什么我们换成位运算,程序会更快的原因。因为不会存在预测失败重新执行的情况。

好像和我程序里写的不一样

public class Thread01 implements Runnable {

private int cnt=10;

@Override

public void run() {

while (cnt>0){

System.out.println(Thread.currentThread().getName()+" cnt= "+cnt--);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) {

Thread01 thread01=new Thread01();

Thread a=new Thread(thread01,"a");

Thread b=new Thread(thread01,"b");

Thread c=new Thread(thread01,"c");

Thread d=new Thread(thread01,"d");

Thread e=new Thread(thread01,"e");

a.start();

b.start();

c.start();

d.start();

e.start();

}

}

运行这段程序,你会发现有两个线程输出了同样的数字,按道理这是绝对不可能发生的事情。还是计算机惹的锅,因为CPU访问缓存区比访问内存要快得多,所以为了高效,现代计算机往往会在高速缓存区中缓存共享变量。

26613cd56b931e08deeb4cacdf677329.png

也就是说,大家各搞各的,这与cache的写回操作有关(保险的办法当然是写直达,但咱不是为了快嘛,所以就出问题了)。有可能你修改的时候,来不及通知我这个值已经不能用了,要重新获取,最后的结果就是输出了两个一模一样的值。

多线程的东西很多:重排序,内存屏障,CAS....感兴趣的同学可以查阅相关资料,这篇博文只是抛砖引玉而已。

原文:https://www.cnblogs.com/AD-milk/p/13171059.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值