什么是蹦床功能?
During recent discussions at work, someone referred to a trampoline function.
I have read the description at Wikipedia. It is enough to give a general idea of the functionality, but I would like something a bit more concrete.
Do you have a simple snippet of code that would illustrate a trampoline?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
还有 LISP 意义上的“蹦床”,如 Wikipedia 上所述:
假设我们正在使用 Javascript,并希望以连续传递风格编写朴素的斐波那契函数。 我们这样做的原因并不相关——例如将Scheme移植到JS,或者使用我们必须使用的CPS来调用服务器端函数。
因此,第一次尝试是,
但是,在 Firefox 中使用
n = 25
运行此命令会出现错误“递归过多!”。 现在这正是蹦床解决的问题(Javascript 中缺少尾部调用优化)。 让我们返回
一条指令(thunk)来调用该函数,而不是对函数进行(递归)调用,以便在循环中进行解释。There is also the LISP sense of 'trampoline' as described on Wikipedia:
Let us say we are using Javascript and want to write the naive Fibonacci function in continuation-passing-style. The reason we would do this is not relevant - to port Scheme to JS for instance, or to play with CPS which we have to use anyway to call server-side functions.
So, the first attempt is
But, running this with
n = 25
in Firefox gives an error 'Too much recursion!'. Now this is exactly the problem (missing tail-call optimization in Javascript) that trampolining solves. Instead of making a (recursive) call to a function, let usreturn
an instruction (thunk) to call that function, to be interpreted in a loop.让我添加一些用不同语言使用蹦床实现的阶乘函数的示例:
Scala:
Java:
C(不幸的是没有大数字实现):
Let me add few examples for factorial function implemented with trampolines, in different languages:
Scala:
Java:
C (unlucky without big numbers implementation):
我给你举一个我在在线游戏反作弊补丁中使用的例子。
我需要能够扫描游戏正在加载的所有文件以进行修改。 因此,我发现执行此操作的最可靠方法是对 CreateFileA 使用蹦床。 因此,当游戏启动时,我会使用 GetProcAddress 找到 CreateFileA 的地址,然后我会修改函数的前几个字节并插入会跳转到我自己的“trampoline”函数的汇编代码,我会在其中执行一些操作,并且然后我会跳回到 CreateFile 中 jmp 代码之后的下一个位置。 要可靠地做到这一点比这要困难一些,但基本概念就是挂钩一个函数,强制它重定向到另一个函数,然后跳回原始函数。
编辑:微软有一个针对此类事物的框架,您可以查看。 称为绕道
I'll give you an example that I used in an anti-cheat patch for an online game.
I needed to be able to scan all files that were being loaded by the game for modification. So the most robust way I found to do this was to use a trampoline for CreateFileA. So when the game was launched I would find the address for CreateFileA using GetProcAddress, then I would modify the first few bytes of the function and insert assembly code that would jump to my own "trampoline" function, where I would do some things, and then I would jump back to the next location in CreateFile after my jmp code. To be able to do it reliably is a little trickier than that, but the basic concept is just to hook one function, force it to redirect to another function, and then jump back to the original function.
Edit: Microsoft has a framework for this type of thing that you can look at. Called Detours
以下是嵌套函数的示例:
compar
不能是外部函数,因为它使用nbytes
,而该函数仅在sort_bytes
调用期间存在。 在某些架构上,一个小的存根函数(trampoline)是在运行时生成的,并且包含当前调用sort_bytes
的堆栈位置。 调用时,它会跳转到compar
代码,并传递该地址。在像 PowerPC 这样的架构上不需要这种混乱,其中 ABI 指定函数指针实际上是一个“胖指针”,一个包含指向可执行代码的指针和另一个指向数据的指针的结构。 然而,在 x86 上,函数指针只是一个指针。
Here's an example of nested functions:
compar
can't be an external function, because it usesnbytes
, which only exists during thesort_bytes
call. On some architectures, a small stub function -- the trampoline -- is generated at runtime, and contains the stack location of the current invocation ofsort_bytes
. When called, it jumps to thecompar
code, passing that address.This mess isn't required on architectures like PowerPC, where the ABI specifies that a function pointer is actually a "fat pointer", a structure containing both a pointer to the executable code and another pointer to data. However, on x86, a function pointer is just a pointer.
我目前正在尝试为Scheme解释器实现尾部调用优化的方法,因此目前我正在尝试弄清楚蹦床对我来说是否可行。
据我了解,它基本上只是由蹦床函数执行的一系列函数调用。 每个函数都称为 thunk,并返回计算中的下一步,直到程序终止(空延续)。
这是我为提高对蹦床的理解而编写的第一段代码:
结果:
I am currently experimenting with ways to implement tail call optimization for a Scheme interpreter, and so at the moment I am trying to figure out whether the trampoline would be feasible for me.
As I understand it, it is basically just a series of function calls performed by a trampoline function. Each function is called a thunk and returns the next step in the computation until the program terminates (empty continuation).
Here is the first piece of code that I wrote to improve my understanding of the trampoline:
results in:
现在 C# 具有 本地函数,保龄球游戏编码型可以用蹦床优雅地解决:
该方法
Game.RollMany
会通过多次掷骰来调用:如果没有备用或罢工,通常为 20 次掷骰。第一行立即调用trampoline函数:
return Trampoline(1, 0, rs.ToList());
。 该局部函数递归遍历 rolls 数组。 本地函数(trampoline)允许遍历从两个附加值开始:从frame
1 和rsf
(到目前为止的结果)0 开始。在本地函数中是三元运算符,处理五种情况:
继续遍历是通过再次调用蹦床来完成的,但现在使用更新的值。
有关更多信息,请搜索:“尾递归累加器”。 请记住,编译器不会优化尾递归。 因此,尽管这个解决方案可能很优雅,但它可能不会是禁食的。
Now that C# has Local Functions, the Bowling Game coding kata can be elegantly solved with a trampoline:
The method
Game.RollMany
is called with a number of rolls: typically 20 rolls if there are no spares or strikes.The first line immediately calls the trampoline function:
return Trampoline(1, 0, rs.ToList());
. This local function recursively traverses the rolls array. The local function (the trampoline) allows the traversal to start with two additional values: start withframe
1 and thersf
(result so far) 0.Within the local function there is ternary operator that handles five cases:
Continuing the traversal is done by calling the trampoline again, but now with updated values.
For more information, search for: "tail recursion accumulator". Keep in mind that the compiler does not optimize tail recursion. So as elegant as this solution may be, it will likely not be the fasted.
对于 C,蹦床将是一个函数指针:
编辑:编译器将隐式生成更多深奥的蹦床。 其中一种用途是跳转表。 (尽管您开始尝试生成复杂的代码越往下,显然有更复杂的代码。)
For C, a trampoline would be a function pointer:
Edit: More esoteric trampolines would be implicitly generated by the compiler. One such use would be a jump table. (Although there are clearly more complicated ones the farther down you start attempting to generate complicated code.)