Skip to content

Commit 2befa93

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Add fannkuch-redux benchmark
Summary: Add the fannkuch-redux benchmark from the Computer Language Benchmarks Game. This benchmark does heavy list indexing in tight loops -- every iteration of the inner flip-counting function accesses perm[i], perm[j], perm[0], perm[k] repeatedly. The hot inner function (count_flips) is split out so it gets called ~3.6M times for n=10, ensuring it hits the JIT auto-compile threshold. However the fannkuch function does not, and that _is_ also hot. Force compiling it saves another 100-200ms, but we don't want to be doing that. Initial benchmark results (n=10, 1 iteration): JIT: 2.09s No JIT: 2.29s (1.10x speedup with JIT) Reviewed By: mpage Differential Revision: D96742410 fbshipit-source-id: 8ec85c2d608cf013ea6d96c8755121fb584144ad
1 parent fcf5a54 commit 2befa93

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

cinderx/benchmarks/fannkuch.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
"""
4+
Fannkuch-redux benchmark.
5+
6+
Repeatedly generates permutations and counts "pancake flips" (reversals
7+
of a prefix of the permutation). Exercises tight integer loops with
8+
heavy list indexing -- every iteration does multiple perm[i] and
9+
count[i] accesses, making this an excellent stress test for integer
10+
unboxing and IndexUnbox performance.
11+
12+
Based on the classic "fannkuch-redux" benchmark from the Computer Language
13+
Benchmarks Game.
14+
"""
15+
16+
import sys
17+
18+
import cinderx.jit
19+
20+
21+
def count_flips(perm1, perm, n):
22+
k = perm1[0]
23+
if not k:
24+
return 0
25+
flips = 1
26+
# Copy perm1 into perm for flipping
27+
for i in range(n):
28+
perm[i] = perm1[i]
29+
while True:
30+
# Reverse perm[0:k+1]
31+
i = 1
32+
j = k - 1
33+
while i < j:
34+
perm[i], perm[j] = perm[j], perm[i]
35+
i += 1
36+
j -= 1
37+
# Swap endpoints
38+
perm[0], perm[k] = perm[k], perm[0]
39+
k = perm[0]
40+
if not k:
41+
break
42+
flips += 1
43+
return flips
44+
45+
46+
def fannkuch(n):
47+
perm = [0] * n
48+
perm1 = list(range(n))
49+
count = [0] * n
50+
max_flips = 0
51+
checksum = 0
52+
sign = 1
53+
m = n - 1
54+
55+
while True:
56+
flips = count_flips(perm1, perm, n)
57+
if flips > max_flips:
58+
max_flips = flips
59+
checksum += flips * sign
60+
61+
# Generate next permutation (Johnson-Trotter algorithm variant)
62+
if sign == 1:
63+
perm1[0], perm1[1] = perm1[1], perm1[0]
64+
sign = -1
65+
else:
66+
perm1[1], perm1[2] = perm1[2], perm1[1]
67+
sign = 1
68+
r = 2
69+
while r < n:
70+
if count[r] < r:
71+
break
72+
count[r] = 0
73+
r += 1
74+
else:
75+
return checksum, max_flips
76+
77+
# Rotate perm1[0:r+1] right by one
78+
perm0 = perm1[0]
79+
for i in range(r):
80+
perm1[i] = perm1[i + 1]
81+
perm1[r] = perm0
82+
83+
count[r] += 1
84+
85+
86+
class Fannkuch(object):
87+
def run(self, iterations):
88+
for _ in range(iterations):
89+
n = 10
90+
checksum, max_flips = fannkuch(n)
91+
# Expected values for n=10, determined empirically.
92+
if checksum != 73196:
93+
return False
94+
if max_flips != 38:
95+
return False
96+
return True
97+
98+
99+
if __name__ == "__main__":
100+
cinderx.jit.auto()
101+
102+
num_iterations = 1
103+
if len(sys.argv) > 1:
104+
num_iterations = int(sys.argv[1])
105+
Fannkuch().run(num_iterations)

0 commit comments

Comments
 (0)