Algorithm 关于用户自制和内置方案之间的程序速度(与SICP练习1.23相关)

Algorithm 关于用户自制和内置方案之间的程序速度(与SICP练习1.23相关),algorithm,scheme,time-complexity,primes,sicp,Algorithm,Scheme,Time Complexity,Primes,Sicp,//我的问题太长了,所以我减少了回答 在scheme中,用户创建的过程比内置过程消耗更多的时间? (如果两者的功能相同) //这是我的简短问题。 //下面是长版问题 EX 1.23是问题(如下),为什么(下一个)程序不比(+1)快两倍 这是我的猜测 原因1:(next)包含“if”(特殊形式),它会消耗时间 原因2:函数调用消耗时间 说理由1是对的 我想知道原因2也是对的 所以我重写了(下一个)程序。我没有使用“如果”,只是在使用(下一步)之前检查了一次数字除以2(所以(下一步)过程只做+2)。

//我的问题太长了,所以我减少了回答

在scheme中,用户创建的过程比内置过程消耗更多的时间? (如果两者的功能相同)

//这是我的简短问题。 //下面是长版问题

EX 1.23是问题(如下),为什么(下一个)程序不比(+1)快两倍

这是我的猜测

原因1:(next)包含“if”(特殊形式),它会消耗时间

原因2:函数调用消耗时间

说理由1是对的

我想知道原因2也是对的

所以我重写了(下一个)程序。我没有使用“如果”,只是在使用(下一步)之前检查了一次数字除以2(所以(下一步)过程只做+2)。我重新测量了时间。它比以前快了,但仍然不是2。所以我又重写了一遍。我将(下一个)更改为(+num 2)。最后它变成了2或几乎是2。我想为什么。这就是我猜“原因2”的原因。我想知道什么是正确答案

另外,我也很好奇为什么一些素数比其他素数测试(非常?)快?这是没有意义的,因为若一个数n是素数,那个么这个过程应该从2到sqrt(n)。但有些数字的测试速度更快。你知道为什么有些素数测试得更快吗

练习1.23。本节开头显示的最小除数程序进行了大量不必要的测试:在检查数字是否可以被2整除后,检查它是否可以被任何较大的偶数整除是没有意义的。这表明用于测试除数的值不应该是2,3,4,5,6,…,而是2,3,5,7,9。。。。要实现此更改,请定义一个过程,如果输入等于2,则返回3,否则返回输入加2。修改最小除数过程以使用(下一个测试除数)而不是(+测试除数1)。使用包含此最小除数修改版本的定时素数测试,对练习1.22中发现的12个素数中的每一个运行测试。由于这种修改使测试步骤的数量减少了一半,所以您应该期望它的运行速度是原来的两倍。这一预期得到证实了吗?如果不是,观察到的两种算法的速度比是多少,您如何解释它与2不同的事实

书在哪里:

你的长问题和短问题实际上解决了两个不同的问题

EX 1.23是问题(如下),为什么(下一个)程序不比(+1)快两倍

您提供了两个可能的原因来解释相对缺乏速度提升(30%的速度提升已经是一个很好的成就):

  • 显然使用了函数
    next
    而不是简单的算术表达式(正如我从您的解释中理解的)

  • 在该
    下一个
    函数中包含一个测试

第一个原因是一种错觉:
(+1)
是一个递增其参数的函数,因此在这两种情况下都有一个函数调用(尽管递增函数肯定是内置函数,这一点由您的另一个问题解决)

第二个原因确实相关:代码块中的任何测试都会在代码流中引入潜在的重定向(从当前执行的指令跳到程序中的其他地址),这可能会导致延迟。请注意,这类似于函数调用,函数调用也会导致地址跳转,因此这两个原因实际上只解决一个潜在原因

关于您的简短问题,内置函数实际上通常更快,因为编译器能够在某些情况下对它们进行特殊处理。这是由于以下事实:

  • 通过了解内置函数的语义,编译器设计人员能够包括与这些内置函数的代数属性相关的特殊规则,例如,在单个调用中融合连续增量,或者抑制顺序调用的增量和减量的组合

  • 如果未进行优化,内置函数调用将转换为本机机器码函数调用,这可能不必遵守所有调用约定规则。如果scheme编译器从源代码生成机器代码,那么可能只有边际收益,但如果它生成所谓的字节码,收益可能相当可观,因为用户编写的函数将转换为该字节码格式,并且仍然需要某种形式的解释。如果您只使用解释器,那么增益就更为重要


我认为这是高度依赖于实现和设置的。在许多实现中有不同种类的优化,而在一些实现中没有。为了获得最佳性能,您可能需要编译代码或使用设置来减少调试信息/堆栈跟踪。在一个实现中获得最佳性能可能会使另一个实现的性能恶化

原始过程通常编译为本机的,在某些实现中,如ikarus,它甚至是内联的。(当您执行
(映射汽车lst)
ikarus将其更改为
(映射(lambda(x)(汽车x))lst)
,因为
汽车
不是一个过程)lambda应该很便宜。。请记住,许多scheme实现会将代码更改为CPS,这是一个过程调用体中每个表达式的一个过程调用。它永远不会像机器代码那样快,因为它需要加载闭包变量

要检查两个选项中哪一个适合您的实现,请执行与最初相同的操作。没有
if
,只需增加参数。现在的区别是额外的呼叫,而不是其他。然后,您可以内联
next
,方法是直接在过程中写入代码并用参数替换操作数。它是否仍然较慢,那么它是
if