Why Sponsor Oils? | blog | oilshell.org
This post shows four solutions to an instance of the function-directed enum labeling problem. Today I'll show the code, and tomorrow I'll explain how they were discovered.
It's important to understand that these four functions are not equivalent: each one requires a different enum labeling. But they have the same structure, which is what we care about.
For brevity, the first three are shown in Python without the enum labeling, which looks like a random assignment of integers. The last one is shown in C++ with the enum labeling and proof that it works.
Notes:
The idea here is that id & (-id - 23)
is close to what we want, and then we
fix it up with some ad hoc conditionals. The naive version would have around
21 tests; this has 6.
def LookupKind1(id):
kind = id & (-id - 23)
if kind: return kind
if id <= 1: return 20
if id <= 9: return 44
if id <= 40: return 48
if id <= 72: return 50
if id <= 105: return 4
return 2
The expression (id & 230) & (id + 13)
has 3 operations instead of 2, but it
only requires one conditional fix.
def LookupKind2(id):
return (id & 230) & (id + 13) if id else 32
I used Veedrac's method to find this solution with 5 instructions and no branches. In the last post I talked about branch misprediction a bit.
def LookupKind3(id):
return (-id + 12) & (id ^ 6) & (id + 14)
This solution has only 4 instructions, and still no branches.
def LookupKind4(id):
return 175 & id & ((id ^ 173) + 11)
Here it is in strongly-typed C++:
Kind LookupKind(Id id) {
int i = static_cast<int>(id);
int k = 175 & i & ((i ^ 173) + 11);
return static_cast<Kind>(k);
}
The file ik7.cc has the full enum labeling and an exhaustive test of all 221 lookups. Run it like this:
$ ./run.sh ik7 + mkdir -p _tmp + c++ -std=c++11 -O3 -o _tmp/ik7 ik7.cc + _tmp/ik7 PASSED // This is printed after 221 tests pass
Tomorrow I'll explain how these solutions were discovered. You can also take a peek at the reddit thread, but I'll explain it a bit more concisely and present the "metaprogramming" code.