diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | __pycache__/mini_scalene.cpython-311.pyc | bin | 13865 -> 0 bytes | |||
| -rw-r--r-- | __pycache__/replacement_poll_selector.cpython-311.pyc | bin | 2546 -> 0 bytes | |||
| -rw-r--r-- | __pycache__/sitecustomize.cpython-311.pyc | bin | 417 -> 0 bytes | |||
| -rw-r--r-- | mini_scalene.py | 15 | ||||
| -rw-r--r-- | replacement_epoll_selector.py | 27 | ||||
| -rw-r--r-- | replacement_poll_selector.py | 41 | ||||
| -rw-r--r-- | tests/overload.py | 29 | ||||
| -rw-r--r-- | tests/simult.py | 17 | ||||
| -rw-r--r-- | tests/successive_sleep.py | 15 |
10 files changed, 39 insertions, 106 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/__pycache__/mini_scalene.cpython-311.pyc b/__pycache__/mini_scalene.cpython-311.pyc Binary files differdeleted file mode 100644 index 24b7268..0000000 --- a/__pycache__/mini_scalene.cpython-311.pyc +++ /dev/null diff --git a/__pycache__/replacement_poll_selector.cpython-311.pyc b/__pycache__/replacement_poll_selector.cpython-311.pyc Binary files differdeleted file mode 100644 index b346241..0000000 --- a/__pycache__/replacement_poll_selector.cpython-311.pyc +++ /dev/null diff --git a/__pycache__/sitecustomize.cpython-311.pyc b/__pycache__/sitecustomize.cpython-311.pyc Binary files differdeleted file mode 100644 index 4f5ff5a..0000000 --- a/__pycache__/sitecustomize.cpython-311.pyc +++ /dev/null diff --git a/mini_scalene.py b/mini_scalene.py index 569d7c0..f7a8ca9 100644 --- a/mini_scalene.py +++ b/mini_scalene.py @@ -1,3 +1,5 @@ +import selectors + import sys import argparse import threading @@ -13,6 +15,7 @@ from typing import ( ) from types import FrameType from collections import defaultdict +import replacement_epoll_selector the_globals = { '__name__': '__main__', @@ -64,7 +67,6 @@ class MiniScalene(object): profile_async = True def __init__(self): - import replacement_poll_selector signal.signal(signal.SIGPROF, self.cpu_signal_handler) signal.setitimer(signal.ITIMER_PROF, @@ -80,12 +82,16 @@ class MiniScalene(object): @staticmethod def start(profile_async): MiniScalene.profile_async = profile_async - atexit.register(MiniScalene.exit_handler) @staticmethod - def exit_handler(): - '''Turn off profiling signals & pretty-print profiling information.''' + def stop(): + '''Turn off profiling signals''' MiniScalene.disable_signals() + MiniScalene.exit_handler() + + @staticmethod + def exit_handler(): + '''Pretty-print profiling information.''' # If we've collected any samples, dump them. print("CPU usage (Python):") if MiniScalene.total_cpu_samples > 0: @@ -250,6 +256,7 @@ def main(): code = compile(fp.read(), args.script, "exec") MiniScalene().start(args.async_off) exec(code, the_globals) + MiniScalene().stop() except Exception: traceback.print_exc() diff --git a/replacement_epoll_selector.py b/replacement_epoll_selector.py new file mode 100644 index 0000000..545bbc6 --- /dev/null +++ b/replacement_epoll_selector.py @@ -0,0 +1,27 @@ +import selectors +import sys +import time +from typing import List, Tuple + + +class ReplacementEpollSelector(selectors.EpollSelector): + def select( + self, timeout=None + ) -> List[Tuple[selectors.SelectorKey, int]]: + start_time = time.perf_counter() + if not timeout or timeout < 0: + interval = sys.getswitchinterval() + else: + interval = min(timeout, sys.getswitchinterval()) + while True: + selected = super().select(interval) + if selected or timeout == 0 or not timeout: + return selected + end_time = time.perf_counter() + if end_time - start_time >= timeout: + return [] # None + +ReplacementEpollSelector.__qualname__ = ( + "replacement_epoll_selector.ReplacementEpollSelector" +) +selectors.DefaultSelector = ReplacementEpollSelector diff --git a/replacement_poll_selector.py b/replacement_poll_selector.py deleted file mode 100644 index 0813a66..0000000 --- a/replacement_poll_selector.py +++ /dev/null @@ -1,41 +0,0 @@ -import selectors -import sys -import threading -import time -from typing import List, Optional, Tuple - -from mini_scalene import MiniScalene - - -@MiniScalene.shim -def replacement_poll_selector(mini_scalene: MiniScalene) -> None: - """ - A replacement for selectors.PollSelector that - periodically wakes up to accept signals - """ - - class ReplacementPollSelector(selectors.PollSelector): - def select( - self, timeout: Optional[float] = -1 - ) -> List[Tuple[selectors.SelectorKey, int]]: - tident = threading.get_ident() - start_time = time.perf_counter() - if not timeout or timeout < 0: - interval = sys.getswitchinterval() - else: - interval = min(timeout, sys.getswitchinterval()) - while True: - scalene.set_thread_sleeping(tident) - selected = super().select(interval) - scalene.reset_thread_sleeping(tident) - if selected or timeout == 0: - return selected - end_time = time.perf_counter() - if timeout and timeout != -1: - if end_time - start_time >= timeout: - return [] # None - - ReplacementPollSelector.__qualname__ = ( - "replacement_poll_selector.ReplacementPollSelector" - ) - selectors.PollSelector = ReplacementPollSelector # type: ignore diff --git a/tests/overload.py b/tests/overload.py deleted file mode 100644 index 9bba9a1..0000000 --- a/tests/overload.py +++ /dev/null @@ -1,29 +0,0 @@ -import asyncio -import time - -async def busy_task(): - i = 0 - while i < 100: - i += 1 - await asyncio.sleep(3.0) - return 1 - -async def main(): - tasks = [asyncio.create_task(busy_task()) for i in range(5)] - - start_time = time.time() - try: - # this is to prevent waiting in 'select' all day, - # which makes the python intepreter not respond to - # mini-scalene / SCALENE - while True: - if time.time() - start_time > 4.0: - break - # print(asyncio.all_tasks()) - await asyncio.sleep(0) # yield - except KeyboardInterrupt: - pass - finally: - print("Done.") - -asyncio.run(main()) diff --git a/tests/simult.py b/tests/simult.py deleted file mode 100644 index 49d3e4b..0000000 --- a/tests/simult.py +++ /dev/null @@ -1,17 +0,0 @@ -import asyncio - - -async def count(): - print("before") - await asyncio.sleep(3) - print("after") - - -async def main(): - await asyncio.gather(count(), count(), count()) - i = 0 - while i < 3000000: - i += 1 - print("done") - -asyncio.run(main()) diff --git a/tests/successive_sleep.py b/tests/successive_sleep.py deleted file mode 100644 index fc9df70..0000000 --- a/tests/successive_sleep.py +++ /dev/null @@ -1,15 +0,0 @@ -import time - - -def slow(): - time.sleep(0.4) - - -def main(): - for i in range(3): - time.sleep(0.2) - slow() - - -if __name__ == "__main__": - main() |
