Parrot provides two opcodes to branch on the truthness of a
register: if
and unless
. It looks
like that it's just a matter of taste, which one to use. Is it
really?
Not with modern CPUs with branch prediction rules and
with JIT code inside tight inner loops. Below are two versions
of the ackermann function, one written with if
and one using unless
. (If you are missing the
loop in the code, well, that's coming from tailcall
optimization, turned on with -Oc
.)
$ cat ack-if.pir
.sub ack
.param int x
.param int y
if x goto a1
inc y
.return (y)
a1:
if y goto a2
dec x
.return ack(x, 1)
a2:
dec y
y = ack(x, y)
dec x
.return ack(x, y)
.end
$ cat ack-unless.pir
.sub ack
.param int x
.param int y
unless x goto a1
unless y goto a2
dec y
y = ack(x, y)
dec x
.return ack(x, y)
a1:
inc y
.return (y)
a2:
dec x
.return ack(x, 1)
.end
And here are the timings of both with ./parrot -Oc -Cj ack-xx.pir 12
on an AMD X2@2000:
ack-if 2.9 s
ack-unless 2.4 s
Branches with positive branch offsets (downwards) are considered to be likely
not taken, that is the fall-through case is the default one. As there are a lot
more positive numbers than the one zero, the unless
is the better choice
here (for some CPUs, YMMV). Well, that's at least my
conclusion for now. But there is another issue: branch
alignment. I've not yet experimented with
different label alignments, but I know that alignment can
cause timing differences like above too.
A similar dichotomy results from "while" vs "until". So, both are needed.sub using_if {
$value = 3;
"bogus" if not $value;
}
sub using_unless {
$value = 3;
"bogus" unless $value;
}
my $result_if = using_if(); # will be "false" (result of "not $value")
my $result_unless = using_unless(); # will be 3 (result of $value)