Found while writing an ordinary program with Pyfun 0.6.0 — a Scrabble move
generator's cross-check, asked 4.4 million times per position:
let allowsLetter checks rc l =
match Map.tryFind rc checks:
case None: true
case Some s: Set.contains l s
This is the language's way to say "look it up, and here is what to do in
each case". It lowers to a helper that allocates a Some, followed by a
structural match that takes it apart again:
def _pf_map_try_find(k, m):
if k in m:
return Some(m.get(k))
return None_()
def allowsLetter(checks, rc, l):
match _pf_map_try_find(rc, checks):
case None_():
return True
case Some(s):
return l in s
case _:
raise RuntimeError("non-exhaustive match")
The tell
The Python a person would write is
def allowsLetter(checks, rc, l):
if rc in checks:
return l in checks[rc]
return True
and hand-editing the emitted module to that took the generator's hot case
from 8.21s to 7.96s — on top of the Option representation fixes in #87 —
with identical output. Nothing about the source changes; the Option was
only ever a way to carry the answer from the helper to the match, and
when the two are adjacent it need not exist.
Why it happens
"Map.tryFind" lowers to the helper unconditionally
(src/lowering/mod.rs:2476), and the match lowering
(src/lowering/mod.rs:1234, 1417, 3170) never looks at what the
scrutinee is. A peephole in the match lowering for a scrutinee that is a
fully applied Map.tryFind k m (likewise List.get i xs, String.get,
List.head) with exactly the None/Some v arms could bind v from
m[k] under if k in m: and put the arms in the two branches. The
Option-returning helper stays for every other use — it only disappears
where the match consumes it on the spot, which is the shape option { }
was added to encourage.
Found while writing an ordinary program with Pyfun 0.6.0 — a Scrabble move
generator's cross-check, asked 4.4 million times per position:
This is the language's way to say "look it up, and here is what to do in
each case". It lowers to a helper that allocates a
Some, followed by astructural
matchthat takes it apart again:The tell
The Python a person would write is
and hand-editing the emitted module to that took the generator's hot case
from 8.21s to 7.96s — on top of the
Optionrepresentation fixes in #87 —with identical output. Nothing about the source changes; the
Optionwasonly ever a way to carry the answer from the helper to the
match, andwhen the two are adjacent it need not exist.
Why it happens
"Map.tryFind"lowers to the helper unconditionally(
src/lowering/mod.rs:2476), and the match lowering(
src/lowering/mod.rs:1234,1417,3170) never looks at what thescrutinee is. A peephole in the match lowering for a scrutinee that is a
fully applied
Map.tryFind k m(likewiseList.get i xs,String.get,List.head) with exactly theNone/Some varms could bindvfromm[k]underif k in m:and put the arms in the two branches. TheOption-returning helper stays for every other use — it only disappears
where the match consumes it on the spot, which is the shape
option { }was added to encourage.