Skip to content

Commit

Permalink
added "mypy stack size" experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
OriRoth committed Aug 17, 2022
1 parent c458d47 commit 2dd19b2
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 10 deletions.
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Here is one possible script (also found in `typing_machines/app.py`):

```python
from typing_machines.app import * # import application

with open("example.py", "w") as python_file: # write palindromes machine and input "abbabba"
python_file.write(encode(Algorithm.Grigore, palindromes, "abbabba"))
sleep(1) # wait for write operation
Expand All @@ -45,16 +44,9 @@ makes `mypy` throw a segmentation fault:

```python
from typing import TypeVar, Generic

T = TypeVar("T", contravariant=True)


class N(Generic[T]): ...


class C(N[N["C"]]): ...


_: N[C] = C()
```

Expand All @@ -64,5 +56,5 @@ certain programs is unavoidable.
## What's new?

We introduce an alternative construction that is supposed to compile much faster for large inputs. You can try the new
construction by typing `R` instead of
`G` in the first argument to `app.py`.
construction by using `Algorithm.Roth` instead of
`Algorithm.Grigore` in the script above.
Empty file.
96 changes: 96 additions & 0 deletions typing_machines/experiment/stack_size_experiment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from os import remove
from random import Random
from resource import RLIMIT_STACK, setrlimit
from subprocess import Popen, DEVNULL
from time import sleep
from typing import Callable, List, Tuple, Iterable

import matplotlib.pyplot as plt

from typing_machines.app import encode, Algorithm
from typing_machines.examples.machines import palindromes


def binary_search(le: Callable[[int], bool]) -> int:
"""
Finds a natural number according to the given "lower equals" predicate.
Returns -1 if no number is found.
"""
l: int = -1
u: int = 1
while le(u):
l = u
u *= 2
u -= 1
while l < u:
m: int = (l + u) // 2 + (l + u) % 2
if le(m):
l = m
else:
u = m - 1
return l


def get_random_palindrome(n: int) -> str:
"""
Returns a random palindrome over {a, b} of length n.
Always returns the same word for a given n.
"""
random: Random = Random(n)
palindrome: str = ""
while n > 0:
l: str = "a" if random.getrandbits(1) else "b"
palindrome = l + palindrome + l
n -= 1
return palindrome


def get_stack_size(algorithm: Algorithm, input_word: str) -> int:
"""
Get the call stack size mypy requires to compile the palindromes typing machine with the given
algorithm and input palindrome.
"""

def compiles(n: int) -> bool:
with open("test.py", "w") as python_file:
python_file.write(encode(algorithm, palindromes, input_word))
sleep(1)
stack_size: int = (n + 5) * 1000000
with Popen(["mypy", "test.py"], preexec_fn=lambda: setrlimit(RLIMIT_STACK, (stack_size, stack_size)),
stdout=DEVNULL, stderr=DEVNULL) as p:
retcode = p.wait(timeout=10)
remove("test.py")
return retcode != 0

depth: int = binary_search(compiles)
depth = 5 if depth == -1 else depth + 5
return depth


def run_experiment(algorithm: Algorithm, input_lengths: Iterable[int]) -> List[Tuple[int, int]]:
"""
Find mypy stack sizes for given algorithm and input lengths.
"""
results: List[Tuple[int, int]] = []
for n in input_lengths:
s: int = get_stack_size(algorithm, get_random_palindrome(n))
results.append((n * 2, s))
print(f"mypy requires {s}M stack size with algorithm {algorithm.name} and palindrome of length {n * 2}")
return results


if __name__ == '__main__':
grigore_results: List[Tuple[int, int]] = run_experiment(Algorithm.Grigore, range(5, 9))
print("Grigore's results:")
for n, s in grigore_results:
print(f"Grigore\t{n}\t{s}")
roth_results: List[Tuple[int, int]] = run_experiment(Algorithm.Roth, range(5, 46, 5))
print("Roth's results:")
for n, s in roth_results:
print(f"Roth\t{n}\t{s}")
plt.plot([n for n, _ in grigore_results], [s for _, s in grigore_results], color="blue", label="Grigore")
plt.scatter([n for n, _ in grigore_results], [s for _, s in grigore_results], color="blue", marker="o")
plt.plot([n for n, _ in roth_results], [s for _, s in roth_results], color="green", label="Roth")
plt.scatter([n for n, _ in roth_results], [s for _, s in roth_results], color="green", marker="x")
plt.legend(loc="upper right")
plt.show()

0 comments on commit 2dd19b2

Please sign in to comment.