你的问题的答案很简单:
只需按照通常的方式进行,即使用returns 自己展开堆栈。为什么?因为这并不像你想象的那么慢。除非递归中的计算非常简单并且堆栈深度非常高,否则返回永远不会显着影响算法的运行时间。
基本上,您有以下选择:
- 如果可能,请将您的算法转换为迭代算法。
- 将您的算法转换为结束递归,并希望 VM 将重用堆栈帧。那么,递归返回几乎等于一个简单的返回。
- 抛出异常。但是,这将比返回更慢,因为必须构建堆栈跟踪,最终也会遍历堆栈。此外,必须展开堆栈以检查
catches。你什么也没赢。
前两个选项是可行的,但并不总是可行的。但老实说,别想了。从深堆栈中返回并不是让您的算法变慢的部分。如果您的算法具有非常深的递归,那么无论如何您都会遇到问题(堆栈溢出,递归调用的成本)并且应该考虑重写您的算法。如果堆栈深度较低,那么无论如何这都不是问题。
这里有一个简单的 Java 测试程序来说明我的意思:
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class DeepRecursion {
private long returnStartTime;
public int recurse(int i) {
int r = (int)Math.sqrt((double)i); // Some non-trivial computation
if(i == 0) {
returnStartTime = System.currentTimeMillis();
return r;
}
return r + recurse(i-1);
}
public void testRecursion(int i, PrintStream p) {
long startTime = System.currentTimeMillis();
int result = recurse(i);
long endTime = System.currentTimeMillis();
p.println(
"Recursion depth: " + i + " Result: " + result + "\n" +
"Time for recursion" + (returnStartTime - startTime) + "\n" +
"Time for return " + (endTime - returnStartTime) + "\n"
);
}
public void testIteration(int i, PrintStream p) {
long startTime = System.currentTimeMillis();
int result = 0;
for(int k = 0; k <= i; k++) {
int r = (int)Math.sqrt((double)k); // Some non-trivial computation
result += r;
}
long endTime = System.currentTimeMillis();
p.println("Iteration length: " + i + " Result: " + result + "\nTime: " + (endTime - startTime) );
}
public static void main(String[] args) {
DeepRecursion r = new DeepRecursion();
PrintStream nullStream = new PrintStream(new ByteArrayOutputStream());
for(int k = 0; k < 10; k++) {
// Test stack depths from 1024 to 33554432
for(int i = 10; i < 26; i++) {
r.testIteration(1 << i, k == 9 ? System.out : nullStream);
r.testRecursion(1 << i, k == 9 ? System.out : nullStream);
}
}
}
}
它计算一个递归函数,其堆栈深度等于输入参数。该函数计算每个堆栈帧中的平方根,以模拟一些非平凡的计算。它还以迭代方式计算相同的函数。为了预热 JIT,程序首先执行 9 次而不打印结果;只打印第十个结果。这是我的结果(我必须使用-Xss1g 将堆栈大小增加到 1 GB。这是我机器上的结果:
Iteration length: 1024 Result: 21360
Time for iteration: 0
Recursion depth: 1024 Result: 21360
Time for recursion 0
Time for return 0
Iteration length: 2048 Result: 60810
Time for iteration: 0
Recursion depth: 2048 Result: 60810
Time for recursion 0
Time for return 0
Iteration length: 4096 Result: 172768
Time for iteration: 0
Recursion depth: 4096 Result: 172768
Time for recursion 0
Time for return 0
Iteration length: 8192 Result: 490305
Time for iteration: 0
Recursion depth: 8192 Result: 490305
Time for recursion 0
Time for return 0
Iteration length: 16384 Result: 1390016
Time for iteration: 0
Recursion depth: 16384 Result: 1390016
Time for recursion 0
Time for return 0
Iteration length: 32768 Result: 3938198
Time for iteration: 0
Recursion depth: 32768 Result: 3938198
Time for recursion 0
Time for return 0
Iteration length: 65536 Result: 11152256
Time for iteration: 0
Recursion depth: 65536 Result: 11152256
Time for recursion 1
Time for return 0
Iteration length: 131072 Result: 31570201
Time for iteration: 0
Recursion depth: 131072 Result: 31570201
Time for recursion 1
Time for return 0
Iteration length: 262144 Result: 89347840
Time for iteration: 2
Recursion depth: 262144 Result: 89347840
Time for recursion 1
Time for return 1
Iteration length: 524288 Result: 252821886
Time for iteration: 2
Recursion depth: 524288 Result: 252821886
Time for recursion 4
Time for return 1
Iteration length: 1048576 Result: 715304448
Time for iteration: 5
Recursion depth: 1048576 Result: 715304448
Time for recursion 7
Time for return 3
Iteration length: 2097152 Result: 2023619820
Time for iteration: 9
Recursion depth: 2097152 Result: 2023619820
Time for recursion 14
Time for return 4
Iteration length: 4194304 Result: 1429560320
Time for iteration: 18
Recursion depth: 4194304 Result: 1429560320
Time for recursion 29
Time for return 12
Iteration length: 8388608 Result: -986724456
Time for iteration: 36
Recursion depth: 8388608 Result: -986724456
Time for recursion 56
Time for return 28
Iteration length: 16777216 Result: -1440040960
Time for iteration: 72
Recursion depth: 16777216 Result: -1440040960
Time for recursion 112
Time for return 61
Iteration length: 33554432 Result: 712898096
Time for iteration: 145
Recursion depth: 33554432 Result: 712898096
Time for recursion 223
Time for return 123
如您所见,从一百万深度的堆栈返回需要 3 毫秒。更大的堆栈大小会导致更长的时间,可能是由于堆栈不再适合 L3 缓存。但是,如果您需要如此大的堆栈,则无论如何都会遇到问题,如上所述。以 1 GB 的最大堆栈大小运行 Java 并不是最好的主意。任何低于 131072 的堆栈大小甚至都无法以毫秒为单位进行测量。在一个健全的算法中,堆栈应该比这小得多,所以你应该总是没问题。
如您所见,最快的解决方案是迭代解决方案,因此如果非常深的递归太慢,请完全避免它,而不是只跳过返回。
结论
如果递归对您来说太慢,请完全摆脱它。只是跳过返回不会有很大的不同。