用计算证明:我远远低估了编译器

用计算证明:我远远低估了编译器

前情回顾

将正整数扩大1000倍的计算当中,朋友提出了计算机二进制算法理论:

计算机会进行正整数 X 512 + 正整数 X 256 + 正整数 X 128 + 正整数 X 128 + 正整数 X 64 + 正整数 X 32 + 正整数 X 8,一直累加到凑齐 正整数的 1000 倍 为止。

而运算正整数 X 2的10次方 - ( 正整数 X 16 + 正整数 X 8)要比那一串加号更快接近结果。

但是当我使用解释型语言PHP进行验算 如何使用PHP最高效率的将一个正整数扩大一千倍? 后却得出了相反的结论:只进行一次运算比进行两次计算要更快。

于是我猜测可能是编译型语言解释型语言的差异导致的。围绕这个观点展开了论证。

验算

代码部分

代码价值较低,为不影响阅读,置于附录

运算结果

将代码执行了数十次之后我得出了一个有些出乎意料的结论:方案三对方案二只有微乎其微的优势,甚至可以说两者的计算效率没有差距!

以下贴出两个比较有代表性的结果

用计算证明:我远远低估了编译器

用计算证明:我远远低估了编译器

修正运算

改变计算量级

后来我将计算量上升至10亿次,得出的结论也差不多。只靠这个结论我很难说服自己方案三比方案二更值得选择。但是运算结果也不合理:两者明明采取了不同的计算方案,为什么无法产生差距?

与其说计算时间没有差距,不如说现有的运行时间全部都消耗在了for, rand, 和time上。既然如此,那么提升计算复杂度,来降低过程类计算的干扰是否会得出结果呢?

改变计算复杂度

我将随机数的范围取从 999 改为了 9999999999 ,将乘数从 1000 改为了 8,589,934,590(2的33次方减2)同样各进行一千万次计算,在其他处理步骤不变的情况下,计算耗时被明显延长,但两种方案的计算时长依旧没能拉开差距。

按照豆豆告诉我的理论,integer * 8,589,934,590会被解析integer * 4,294,967,296 + integer * 2,147,483,648 ...一直累加30多次来累计到目标乘积,而integer * 8,589,934,592 - integer * 2则避免了这样的运算。试验结论却不能支持这个观点。

再次讨论

带着这样的数据,我再次找到了豆豆。这次修行更进一步的豆豆同学嘲讽我一番后直接甩给我两篇知乎:

我的理解

两种方案的计算之所以没有产生时间差距,是因为两种写法经过编译之后,都运行了同样的算法。

  • 我:我想让编辑器做一个复杂步骤的计算,再做一个简单步骤的计算,用两种计算的耗时差距来证明计算机的算法。

  • 编译器:没想到吧崽种,我早已预判了你的预判!

要将编译器的预判囊括入我的预判,这一点重新升格为大神的豆豆同学也给出了方案

你把你博客上的代码反汇编成汇编语言,看一看,就知道为什么时间差别不大了

但是汇编早已经超出了蹒跚学步初学者的理解范畴。对于当前学习阶段的我而言,通过本次试验和讨论窥探一眼就好,看多了容易掉 SAN 值。日后若窥探到更多的内容,我会再写一篇博客出来,欢迎关注。

附录

附录代码


func main() {

    // 计算次数

    maxI := 10000000

    fmt.Println("方案1:拼接法")

    // 随机数

    rand.Seed(time.Now().UnixNano())

    echoString := ""

    for count := 0; count < 3; count++ {

        // 开始

        start := time.Now()

        echoString += "第" + strconv.Itoa(count + 1) + "次:"

        for i := 0; i < maxI; i++ {

            integer := rand.Intn(999)

            _, _ = strconv.Atoi(strconv.Itoa(integer) + "000")

        }

        // 结束

        end := time.Now()

        betweenTime := float64(end.Sub(start).Nanoseconds()) / 1000000000

        echoString += strconv.FormatFloat(betweenTime, 'f', 10, 64) + "秒"

    }

    fmt.Println(echoString)

    echoString = ""

    fmt.Println("方案2:乘1000")

    for count := 0; count < 3; count++ {

        // 开始

        start := time.Now()

        echoString += "第" + strconv.Itoa(count + 1) + "次:"

        for i := 0; i < maxI; i++ {

            integer := rand.Intn(999)

            _ = integer * 1000

        }

        // 结束

        end := time.Now()

        betweenTime := float64(end.Sub(start).Nanoseconds()) / 1000000000

        echoString += strconv.FormatFloat(betweenTime, 'f', 10, 64) + "秒"

    }

    fmt.Println(echoString)

    echoString = ""

    fmt.Println("方案3:乘以1024")

    for count := 0; count < 3; count++ {

        // 开始

        start := time.Now()

        echoString += "第" + strconv.Itoa(count + 1) + "次:"

        for i := 0; i < maxI; i++ {

            integer := rand.Intn(999)

            _ = (integer * 1024) - (integer * 24)

        }

        // 结束

        end := time.Now()

        betweenTime := float64(end.Sub(start).Nanoseconds()) / 1000000000

        echoString += strconv.FormatFloat(betweenTime, 'f', 10, 64) + "秒"

    }

    fmt.Println(echoString)

}
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!