Open jasigal opened 2 years ago
The example does not actually use non-linear control, so in this case I would hope that Koka compiles it directly to a linear handler even though it is marked ctl. Apparently it does not? Frank might be doing a similar optimization. So this benchmark might be testing the ability to optimize linear behavior rather than actually testing non-linear behavior. To get a good benchmark comparison consider creating an example with actual non-linear control characteristics.
Re: optimizing to linear, Frank pretty much does no optimizations AFAIK.
Re: benchmark which uses non-linear control, here is a graph I already have from my thesis for reverse mode automatic differentiation. It runs code in the handler after a resumed continuation.
which shows the sames quadratic behaviour using Koka 2.4.0. Here's a gist of the code, the reverse-taylor-recip-benchmark.kk
is the program being to produce the graph. If that's too verbose, I can try to make a smaller example.
So I'd say there are two bugs
I don't know if you knew this, but you can keep the effect definition functions as ctl
and use the more restrictive fun
when writing the handler functions for the effect.
Doing this for all of the tail resumptive clauses results in the above graph.
Knowing what I know about how Koka implements handlers I would say that the first bug you mention actually is causing the second bug.
Fortunately it is easy for a user to fix now, and with better analysis, Koka should be able to do this automatically.
I will note that you do need to increase the stack space using ulimit. Maybe it is something that Koka could better document and warn users about. And maybe figure out a better way to use stack space. (Though for the majority of the time, it has no trouble - except for with ctl effects)
Also I compile with -O2 for optimizations.
Here are the necessary changes to your handlers from the gist:
val reverse = handler {
fun ap0(n) -> {
Prop(op0(n), ref(c(0.0)))
}
// No changes to the other two
...
}
val evaluate = handler {
fun ap0(n) -> match(n) {Const(i) -> i}
fun ap1(u,x: float64) -> match(u) {
Negate -> ~x
Sin -> sin(x)
Cos -> cos(x)
Exp -> exp(x)
}
fun ap2(b,x:float64,y: float64) -> match(b) {
Plus -> x + y
Subtract -> x - y
Times -> x * y
Divide -> x / y
}
}
I'm curious how the equivalent frank program performs on the same machine. Would be interesting comparison to see.
The problem
The following Koka code is quadratic w.r.t.
iters
:see this graph and quadratic-results.txt which was produced with the
hyperfine
program. Furthermore, if I takeiters
too high, for example 100,000, I get a segmentation fault. This can increased by increasing the stack size viaulimit
, so I imagine the issue is that the stack space is exceeded.Some solutions
I have found two ways to alleviate this. The first is to change the effect from
ctl
tofun
, i.e.which results in the following graph and linear-results.txt. The second solution is to move the outer handler into the inner handler, i.e
which results in the following graph and linear-results-ctl.txt.
A comparison
I don't think this quadratic behaviour is intrinsically necessary. The following is (hopefully) an equivalent Frank progam to
quadratic
which results in the following graph and quadratic-frank-results.txt.
Comments
The reason I care and want this is I'm implementing automatic differentiation in Koka after doing it in Frank. I found that having multiple versions of the same effect and nesting handlers as above is useful, and I can't mark the effect as
fun
because reverse mode AD requiresctl
. I also don't want to use the second solution because I lose compositionality (or at the very least I need to change the design).