zoukankan      html  css  js  c++  java
  • 学习计算机组成原理有什么用,意义何在?

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

    从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绝对算的上是最精彩的那一个。不过跟本问题相关的是流水线的设计,我们还是先来认识这熟悉又陌生的词语吧。

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

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

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

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

    让我们再来回忆一下之前的实例程序的流程,在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访问缓存区比访问内存要快得多,所以为了高效,现代计算机往往会在高速缓存区中缓存共享变量.

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

    关于Cache写操作是如何影响程序性能的,可以参考 Cache写策略(Cache一致性问题与骚操作)
    多线程的东西很多:重排序,内存屏障,CAS....感兴趣的同学可以查阅相关资料,这篇博文只是抛砖引玉而已。

  • 相关阅读:
    oracle归档日志增长过快处理方法
    Oracle“死锁”模拟
    Oracle 手工清除回滚段的几种方法
    Oracle 一次 锁表 处理小记
    Oracle中如何判断一个字符串是否含有汉字
    机房收费系统验收总结
    hdu 4747 Mex (线段树)
    Java 5 的新标准语法和用法详解集锦
    java类加载器行为[笔记]
    poj1330Nearest Common Ancestors(LCA小结)
  • 原文地址:https://www.cnblogs.com/AD-milk/p/13171059.html
Copyright © 2011-2022 走看看