- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试递归:
def fac
//fac = { int curr, res = 1G -> 1 >= curr ? res : fac( curr - 1, res * curr ) }
fac = { int curr, res = 1G -> 1 >= curr ? res : fac.trampoline( curr - 1, res * curr ) }
fac = fac.trampoline()
def rnd = new Random()
long s = System.currentTimeMillis()
100000.times{ fac rnd.nextInt( 40 ) }
println "done in ${System.currentTimeMillis() - s} ms / ${fac(40)}"
如果我像这样使用它,我会得到这个:
done in 691 ms
如果我取消注释第 #2 行和注释行 #3-4 以删除 trampoline()
并运行它,我得到的数字会显着降低:
done in 335 ms
因此,使用蹦床,递归速度慢了 2 倍。
我错过了什么?
附注
如果我在 Scala 2.12 中运行相同的示例:
def fac( curr:Int, acc:BigInt = 1 ):BigInt = if( 1 >= curr ) acc else fac( curr - 1, curr * acc )
val s = System.currentTimeMillis
for( ix <- 0 until 100000 ) fac( scala.util.Random.nextInt(40).toInt )
println( s"done in ${System.currentTimeMillis - s} ms" )
它执行得更快一点:
done in 178 ms
更新
使用注释将闭包重写为方法:
@groovy.transform.TailRecursive
def fac( int curr, res = 1G ) { 1 >= curr ? res : fac( curr - 1, res * curr ) }
// the rest
给出
done in 164 ms
而且 super 酷。尽管如此,我仍然想了解 trampoline()
:)
最佳答案
如文档中所述,Closure.trampoline()
可防止调用堆栈溢出。
Recursive algorithms are often restricted by a physical limit: the maximum stack height. For example, if you call a method that recursively calls itself too deep, you will eventually receive a
StackOverflowException
.An approach that helps in those situations is by using
Closure
and its trampoline capability.Closures are wrapped in a
TrampolineClosure
. Upon calling, a trampolinedClosure
will call the originalClosure
waiting for its result. If the outcome of the call is another instance of aTrampolineClosure
, created perhaps as a result to a call to thetrampoline()
method, the Closure will again be invoked. This repetitive invocation of returned trampolined Closures instances will continue until a value other than a trampolinedClosure
is returned. That value will become the final result of the trampoline. That way, calls are made serially, rather than filling the stack.
但是,使用蹦床是有代价的。让我们看一下 JVisualVM 示例。
在没有 trampoline()
的情况下运行示例,我们在大约 441 毫秒内得到结果
done in 441 ms / 815915283247897734345611269596115894272000000000
此执行分配约 2,927,550 个对象并消耗约 100 MB 内存。
CPU 有一些事情要做,除了在 main()
和 run()
方法上花费时间之外,它还花费一些周期来强制参数。
trampoline()
用例蹦床的引入确实改变了很多。首先,与之前的尝试相比,它使执行时间几乎慢了两倍。
done in 856 ms / 815915283247897734345611269596115894272000000000
其次,它分配~5,931,470(!!!)个对象并消耗~221 MB 内存。主要区别在于,在前一种情况下,所有执行中都使用了单个 $_main_closure1
,而在使用 Trampoline 的情况下 - 每次调用 trampoline()
方法都会创建:
$_main_closure1
对象CurriedClosure<T>
包裹TrampolineClosure<T>
包装仅此分配就超过 1,200,000 个对象。
如果涉及到CPU,它还有更多的事情要做。只要看一下数字即可:
TrampolineClosure<T>.<init>()
的所有调用都会消耗199 毫秒PojoeMetaMethodSite$PojoCachedMethodSietNoUnwrap.invoke()
的调用,总共会额外消耗 201 毫秒CachedClass$3.initValue()
的所有调用总计额外消耗 98.8 毫秒ClosureMetaClass$NormalMethodChooser.chooseMethod()
的所有调用总计额外消耗 100 毫秒这正是为什么在您的案例中引入蹦床会使代码执行速度慢得多的原因。
@TailRecursive
做得更好呢?简而言之 - @TailRecursive
注释用旧的 while 循环替换了所有闭包和递归调用。 @TailRecursive
的阶乘函数在字节码级别看起来像这样:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package factorial;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.math.BigInteger;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMultiply;
import org.codehaus.groovy.transform.tailrec.GotoRecurHereException;
public class Groovy implements GroovyObject {
public Groovy() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static BigInteger factorial(int number, BigInteger acc) {
BigInteger _acc_ = acc;
int _number_ = number;
try {
while(true) {
try {
while(_number_ != 1) {
int __number__ = _number_;
int var7 = _number_ - 1;
_number_ = var7;
Number var8 = NumberNumberMultiply.multiply(__number__, _acc_);
_acc_ = (BigInteger)ScriptBytecodeAdapter.castToType(var8, BigInteger.class);
}
BigInteger var4 = _acc_;
return var4;
} catch (GotoRecurHereException var13) {
;
}
}
} finally {
;
}
}
public static BigInteger factorial(int number) {
return factorial(number, (BigInteger)ScriptBytecodeAdapter.castToType(1, BigInteger.class));
}
}
我不久前在我的博客上记录了这个用例。如果您想了解更多信息,可以阅读博客文章:
https://e.printstacktrace.blog/tail-recursive-methods-in-groovy/
关于recursion - Groovy 的 Tampoline() 使递归执行速度变慢 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56578937/
我已经在谷歌上搜索这个问题一段时间了,但我还没有找到有效的解决方案。 问题是 SSH 登录到我的服务器突然变得很慢。我可以看到身份验证需要大约 10 秒才能继续,这是我的 ssh 详细日志: Open
我正在使用 AVPlayer 在我的项目中播放在线视频。视频播放良好。现在我想减少/增加视频的 fps。以下是我正在使用的代码: self.asset = [AVAsset assetWithURL:
在 Raspberry Pi 上运行两个使用 python gpio 引脚的程序时,一个变慢。一种是磁传感器,另一种是温湿度传感器。后者是放慢速度的。它不是每 2 秒打印一次温度,而是每 5 到 10
我从 Redis 向我的应用程序提供一个 json,然后我对其进行解码和循环。 这是我从 Redis 提供的 json 的样子: [ { "titel": "test 1",
Ejabberd 版本:19.9.0 在发送 OMEMO 消息时(使用 websockets),例如
我们有相当大的代码库(150 多个项目、400000 多行 Java 代码、一些 Groovy 和 Gradle 代码、一些 Perl 代码、一些 XML、大量 JSP 等)。我设法在 Spring
我在一个网站上工作,您可以在其中创建 svg 艺术品,这意味着您可以动态添加元素、缩放、颜色并移动它们。 问题是,当你开始在他们身上施加阴影时,一切都会开始变慢。对于这个的现场演示,this是我正在开
有没有办法分析 Vim 插件? 当我打开一个大的 .py 时,我的 MacVim 变得越来越慢。我知道我可以取消选择所有插件并逐一重新选择以检查哪个插件是罪魁祸首,但是有没有更快的方法? 我的 dot
我正在构建一个JavaFX应用程序。我知道它使用反射,并且反射可能不如我在代码中构建 UI 时那么快。 所以, 如何设计我的 Controller 以使由反射引起的开销尽可能小? 带/不带 @FXML
我对 UITableViewCell 进行了子类化显示从 1 到 70 的数字。 在每个单元格中,我都在检查中奖号码并检查他们的背景。问题是,经过几次滚动后,tableview 变得非常缓慢,甚至无法
如果我想group_by 和filter 那些在数据集中有任何NA 或factor 值的,我想在 dplyr 中使用 any 函数,但发现它对 NAs 或 factor 运行缓慢(但不是为了寻找任何数
我有一个问题。在我的解决方案中,我需要将数千个数据插入数据库。我正在使用批处理准备语句在一个请求中插入多行。在我调用插入几次之后, hibernate 变得更慢了。 我猜它会在我提交后检查数据库是否有
我从 json url 获取数据,但是当我想加载图像时,速度非常慢! class NewsTableViewController: UITableViewController { var id
我有一个相当简单的托管 Realm 对象 RealmAlertItem由一些字符串和 float 组成。我有一个函数 showAlertNotification()随后被调用(从网络外部触发)并且它依
请参阅下面的表格结构。 CREATE TABLE `oarc` ( `ID` bigint(20) NOT NULL AUTO_INCREMENT, `zID` int(11) NOT N
IntelliJ 慢得像爬行。键之间没有 1-2 个延迟几乎无法打字。我已经更新了堆大小。我在我的 Macbook Pro 上运行大约 2GB RAM。自从它一直在放缓。我已经增加了堆大小,但无济于事
我的 Web 应用程序遇到了性能问题。发现瓶颈是db。应用程序在具有 4 个 CPU 和 2GB RAM 的 LAMP 服务器 (VPS) 上运行。 将新记录插入数据库(包含大约 100.000 条记
我有关于自定义 DispatchQueue 的问题。 我创建了一个队列,并将其用作captureOutput:方法的队列。这是一个代码片段: //At the file header private
我是一名移动 QA。现在我们有一个关于网络响应和 UI 渲染之间的竞争条件的问题。我们猜测如果 UI 渲染比网络响应慢,那么它就会崩溃。 我们已经尝试通过使用 Charles 的本地 map 功能来加
我在 firefox 中遇到了一些奇怪的行为,我正在构建一个单页作品集,作为一名平面设计师,编码一直很困难。我想平滑地控制导航,然后向所有元素添加缩放(最初设计为 1920x1080 全屏)。讲师扔了
我是一名优秀的程序员,十分优秀!