Professional Documents
Culture Documents
за хвост
Владимир Парфиненко, Excelsior @ Huawei
telegram/twitter: @cypok
2
Stack overflow happens
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
3
Stack overflow happens
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
3
Tail recursion to the rescue!
int factorial(int n) {
return factorialTR(n, 1);
}
4
Tail recursion to the rescue!
int factorial(int n) {
return factorialTR(n, 1);
}
4
Tail recursion to the rescue!
int factorial(int n) {
return factorialTR(n, 1);
}
4
Tail Recursion Optimization (TRO)
5
Tail Recursion Optimization (TRO)
acc = acc * n; n = n - 1;
goto start;
}
5
Tail Recursion Optimization (TRO)
acc = acc * n; n = n - 1;
}
}
5
IDEA inspection
6
IDEA inspection
6
Some science in IDEA
static int gcdI(int n, int m) {
while (true) {
if (m == 0) return n;
n = m;
m = n % m;
}
static int gcd(int n, int m) { }
if (m == 0) return n;
return gcd(m, n % m); static int gcdI(int n, int m) {
} while (true) {
if (m == 0) return n;
int n1 = n;
n = m;
m = n1 % m;
}
}
7
IDEA inspection is not a panacea
static void countDown(int start) {
if (start < 0) return;
System.out.println(start); IDEA-288086
countDown(start - 1);
}
int workHard() {
return workHard(); IDEA-288321
}
8
9
Bytecode transformation
static int triangular(int, int);
0: iload_0
1: iconst_1
2: if_icmpgt 7 // if (n <= 1)
5: iload_1
6: ireturn // return acc;
7: iload_0
8: iconst_1
9: isub // (n - 1)
10: iload_1
11: iload_0
12: iadd // (acc + n)
13: invokestatic triangular:(II)I
16: ireturn
10
Bytecode transformation
static int triangular(int, int);
0: iload_0
1: iconst_1
2: if_icmpgt 7 // if (n <= 1)
5: iload_1
6: ireturn // return acc;
7: iload_0
8: iconst_1
9: isub // (n - 1)
10: iload_1
11: iload_0
12: iadd // (acc + n)
13: istore_1 ibessonov/java-tailrec-agent
10
Short circuit operators
0: getstatic weAreThere:Z
3: ifne 12
static volatile boolean weAreThere;
16: iconst_0
17: ireturn
11
12
Optimization predictability
13
Non-annotated tail-recursive functions
14
Annotated non-tail-recursive functions
@tailrec def a(m: Int, n: Int): Int = tailrec fun a(m: Int, n: Int): Int =
if (m < 1) n + 1 if (m < 1) n + 1
else if (n < 1) a(m - 1, 1) else if (n < 1) a(m - 1, 1)
else a(m - 1, a(m, n - 1)) else a(m - 1, a(m, n - 1))
Error: could not optimize @tailrec Warning: Recursive call is not a tail call
annotated method a:
it contains a recursive call
not in tail position
tailrec open fun workHard() { KT-18533
workHard()
}
Error: Tailrec is not allowed on open members
15
Short-circuit operators
16
This substitution
@tailrec
final def printAll(): Unit = {
println(value) KT-15341
if (next != null) next.printAll()
}
17
Kotlin’s imperfection
KT-39500 in catch
18
19
Real solution for virtual calls
class A {
void workHard() {
/* virtual */ workHard();
}
20
Real solution for virtual calls
class A {
void workHard() {
if (this.getClass() == A.class) {
/* special */ workHard();
} else {
/* virtual */ workHard();
}
}
20
Real solution for virtual calls
class A {
void workHard() {
while (true) {
if (this.getClass() == A.class) {
continue;
} else {
/* virtual */ workHard();
}
}
}
20
Real solution for virtual calls
class A {
void workHard() {
if (this.getClass() == A.class) {
while (true) {
// infinite loop
}
} else {
/* virtual */ workHard();
}
}
20
Real solution for virtual calls
class A {
void workHard() {
if (this.vmt[idx] == A::workHard) {
while (true) {
// infinite loop
}
} else {
/* virtual */ workHard();
}
}
class B extends A { }
20
Real solution for virtual calls
class A {
void workHard() {
if (this.vmt[idx] == A::workHard) {
while (true) {
// infinite loop
}
} else {
/* virtual */ workHard();
}
}
class B extends A {
void workHard() { /* ... */ }
}
20
JVMs with TRO
✘ HotSpot, GraalVM
Консервативность? softwareengineering.stackexchange.com/a/272086
✔ OpenJ9
21
22
23
eclipse-openj9/openj9#1126
24
JVMs with TRO
✘ HotSpot, GraalVM
Консервативность? softwareengineering.stackexchange.com/a/272086
✔ OpenJ9
25
JVMs with TRO
✘ HotSpot, GraalVM
Консервативность? softwareengineering.stackexchange.com/a/272086
✔ OpenJ9
✔ Excelsior JET
25
Real motivation for TRO
bit.ly/cypok-tailrec-bench
26
Stack traces problem
static int fact(int n, int acc) { static int fact(int n, int acc) {
if (n <= 1) return acc; while (true) {
return fact(n - 1, Math.multiplyExact(n, acc)); if (n <= 1) return acc;
} acc = Math.multiplyExact(n, acc);
n = n - 1;
}
}
27
What spec says?
/**
* ...
* Some virtual machines may, under some
* circumstances, omit one or more stack JAVA ПОЗВОЛЯЕТ
* frames from the stack trace. ВЫКИДЫВАТЬ ФРЕЙМЫ JVM ЖЕ ТАК НЕ
* In the extreme case, a virtual machine ИЗ СТЕК ТРЕЙСОВ. ДЕЛАЮТ, ВЕРНО?
* that has no stack trace information
* concerning this throwable is permitted
* to return a zero-length array from
* this method.
* ...
*/
public StackTraceElement[] getStackTrace()
ВЕРНО?
28
Everything has its limit
HotSpot’s
-XX:MaxJavaStackTraceDepth=1024
29
Everything has its limit
29
Everything has its limit
HotSpot’s
-XX:-StackTraceInThrowable
29
Not so exceptional exception
30
Not so exceptional exception
30
JVM with TRO
31
32
32
The end.
Владимир Парфиненко, Excelsior @ Huawei
telegram/twitter: @cypok