方案:为什么内部定义比外部定义快?

2024-04-12

我尝试运行下面的程序

(define (odd-internal x)
  (define (even x)
    (if (zero? x)
        #t
        (odd-internal (sub1 x))))
  (if (zero? x)
      #f
      (even (sub1 x))))

(define (odd-external x)
  (if (zero? x)
      #f
      (even (sub1 x))))

(define (even x)
  (if (zero? x)
      #t
      (odd-external (sub1 x))))

(begin (display "Using internal definition\n")
       (time (odd-internal 40000000)))

(begin (display "Using external definition\n")
       (time (odd-external 40000000)))

这是 Racket 中的结果

Using internal definition
cpu time: 166 real time: 165 gc time: 0
#f
Using external definition
cpu time: 196 real time: 196 gc time: 0
#f

在那里您可以看到使用内部定义要快得多。我尝试在 Chez Scheme 上运行,结果类似。这是为什么?


我很惊讶这是一个差异,所以根据 Lexis 答案的评论,我在每个文件中分割了两个版本internal.rkt and external.rkt并以这种方式编译和反编译它们:

raco make external.rkt
raco decompile compiled/external_rkt.zo

这比查看宏步进器中完全扩展的程序更进一步。它看起来非常不适合人类阅读,所以我用最重要的部分原封不动地对它进行了美化:

(define (odd-external x1)
  (if (zero? x1)
      '#f
      (let ((x2 (sub1 x1)))
        (if (zero? x2)
            '#t
            (let ((x3 (sub1 x2)))
              (if (zero? x3)
                  '#f
                  (let ((x4 (sub1 x3)))
                    (if (zero? x4)
                        '#t
                        (let ((x5 (sub1 x4)))
                          (if (zero? x5) '#f (even (sub1 x5))))))))))))

(define (even x1)
  (if (zero? x1)
       '#t
       (let ((x2 (sub1 x1)))
         (if (zero? x2)
           '#f
           (let ((x3 (sub1 x2)))
             (if (zero? x3)
               '#t
               (let ((x4 (sub1 x3)))
                 (if (zero? x4)
                   '#f
                   (let ((x5 (sub1 x4)))
                     (if (zero? x5)
                       '#t
                       (let ((x6 (sub1 x5)))
                         (if (zero? x6)
                           '#f
                           (let ((x7 (sub1 x6)))
                             (if (zero? x7)
                               '#t
                               (odd-external (sub1 x7))))))))))))))))

这里没什么特别的。它将循环展开一定次数并不断折叠。请注意,我们仍然存在相互递归,并且展开次数为 5 次和 7 次。常数甚至被折叠,所以它取代了我的电话(even 399999995)所以编译器也运行了代码 5 轮并放弃了。有趣的是内部版本:

(define (odd-internal x1)
  (if (zero? x1)
      '#f
      (let ((x2 (sub1 x1)))
        (if (zero? x2)
            '#t
            (let ((x3 (sub1 x2)))
              (if (zero? x3)
                  '#f
                  (let ((x4 (sub1 x3)))
                    (if (zero? x4)
                        '#t
                        (let ((x5 (sub1 x4)))
                          (if (zero? x5)
                              '#f
                              (let ((x6 (sub1 x5)))
                                (if (zero? x6)
                                    '#t
                                    (let ((x7 (sub1 x6)))
                                      (if (zero? x7)
                                          '#f
                                          (let ((x8 (sub1 x7)))
                                            (if (zero? x8)
                                                '#t
                                                (odd-internal
                                                 (sub1 x8))))))))))))))))))

它不再是相互递归,因为它在 8 次后调用了自己。每轮进行 8 轮,而另一个版本进行 7 轮,然后是 5 轮。在两轮中,内部版本完成了 16 轮,而另一个版本完成了 12 轮。内部版本的初始调用是(odd-internal '399999992)所以编译器做了8轮才放弃。

我猜想反编译器级别的函数内部的代码是开放编码的,并且每个步骤的代码都非常便宜,这使得调用次数成为速度提高 25% 的原因。毕竟每次递归多 4 次就多 25%,这与计算时间的差异一致。这是基于观察的推测,因此 Lexi 对此发表评论会很有趣。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

方案:为什么内部定义比外部定义快? 的相关文章

  • FindAsync 很慢,但是延迟加载很快

    在我的代码中 我曾经使用加载相关实体await FindAsync 希望我能更好地遵守 C 异步指南 var activeTemplate await exec DbContext FormTemplates FindAsync exec
  • gcc 不太可能使用宏

    我正在编写一段关键代码 其逻辑大致如下 if expression is true do something with extremely low latency before the nuke blows up This branch i
  • 方案字符串追加?递归复制字符串

    设计一个名为 string dup 的程序 它使用一个字符串 s 和一个数字 n 并返回一个由 s n 次连接而成的字符串 每个 s 实例之间有空格 即 string dup a 3 gt a a a 不使用复制 但我想我们可以使用字符串追
  • bool() 和operator.truth() 有什么区别?

    bool https docs python org 3 library functions html bool and operator truth https docs python org 3 library operator htm
  • php字符串是值类型吗?

    为什么php的string是值类型 每次将参数传递给函数时 每次进行赋值时 每次连接都会导致字符串被复制时 它都会被复制到各处 我的 NET 经验告诉我 它似乎效率低下 迫使我几乎在任何地方都使用引用 考虑以下替代方案 替代方案1 This
  • 为什么 cross_val_predict 比 KNeighborsClassifier 的拟合慢得多?

    在 Jupyter 笔记本上本地运行并使用 MNIST 数据集 28k 条目 每个图像 28x28 像素 以下内容为27秒 from sklearn neighbors import KNeighborsClassifier knn clf
  • 方案如何返回多个值?

    我注意到几乎所有方案函数只能返回一个列表作为输出 下面 我想返回邻居的所有相邻节点的多个值 define neighbors l w if and 1 l 1 w list and l 1 w and 1 l w how to output
  • 动态 SQL 和 where case 哪个更好?

    我需要创建一个带有 12 个参数的存储过程 并使用这些参数的不同组合来过滤查询 所有 12 个参数都不是强制性的 就好像我传递 3 5 或 12 个参数取决于用户输入的搜索输入一样 我可以通过两种方式创建 即使用动态 SQL 查询或使用 C
  • HTML5 Canvas 性能:加载图像与绘图

    我正计划使用 javascript canvas 编写一个游戏 我只有一个问题 在加载图像与仅使用 canvas 的方法进行绘图方面 我应该考虑什么样的性能考虑因素 因为我的游戏将使用非常简单的几何图形 圆形 正方形 直线 所以任何一种方法
  • jQuery .getJSON 与 .post 哪一个更快?

    Using getJSON or post 我正在尝试通过仅用于 AJAX 请求的页面发送一些参数 并获取 JSON 或 html 片段中的一些结果 我想知道哪个更快 假设 HTML 文件只是纯布尔文本 true 或 false 正如其他人
  • 加快写入文件的速度

    我已经分析了一些我用 cProfile 继承的遗留代码 我已经做了很多有帮助的更改 例如使用 simplejson 的 C 扩展 基本上 该脚本将数据从一个系统导出到 ASCII 固定宽度文件 每一行都是一条记录 并且有许多值 每行有 71
  • 字符串与 StringBuilder

    我理解之间的区别String and StringBuilder StringBuilder是可变的 但是两者之间有很大的性能差异吗 我正在开发的程序有很多大小写驱动的字符串附加 500 正在使用StringBuilder更好的选择 是的
  • 存储 PHP 数组的首选方法(json_encode 与序列化)

    我需要将多维关联数据数组存储在平面文件中以进行缓存 我偶尔可能会遇到需要将其转换为 JSON 以便在我的 Web 应用程序中使用的情况 但绝大多数时候我会直接在 PHP 中使用该数组 在此文本文件中将数组存储为 JSON 或 PHP 序列化
  • 处理 C++ 中执行时间的大量分析

    我目前正在进行一个科学计算项目 涉及海量数据和复杂算法 因此需要进行大量代码分析 我目前依靠的是
  • 渲染 ThreeJS 应用程序第一帧时的性能问题

    目前 当我渲染以下内容时 我的 ThreeJS 应用程序的性能受到很大影响第一帧 它会导致 Edge 和 IE 11 浏览器冻结 5 秒 并弹出窗口指示 此窗口没有响应 这可能会吓到我的用户 使用 Chrome 的性能分析器 问题似乎来自几
  • 为什么 pandas 在简单的数学运算上比 numpy 更快?

    最近 我观察到 pandas 的乘法速度更快 我在下面的例子中向您展示了这一点 如此简单的操作怎么可能做到这一点 这怎么可能呢 pandas 数据帧中的底层数据容器是 numpy 数组 测量 我使用形状为 10k 10k 的数组 数据框 i
  • 带有闭包的 JavaScript 性能

    var name function n var digits one two three four return digits n var namenew function digits one two three four return
  • 通过增加索引之和来生成排序组合的有效方法

    对于启发式算法 我需要一个接一个地评估特定集合的组合 直到达到停止标准 由于它们很多 目前我正在使用以下内存高效迭代器块生成它们 受到 python 的启发 itertools combinations http docs python o
  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • PhoneGap 1.4 封装 Sencha Touch 2.X - 性能怎么样?

    我正在构建一个多平台平板电脑应用程序 仅使用其 Webview 使用 Phonegap 1 4 对其进行包装 然后使用 Sencha Touch 2 框架发挥我的魔力 我所说的多平台是指 iOS 5 X 和 Android 3 0 目前 到

随机推荐