shows high-CPU C code hogging the GIL
if you remove the with nogil decorator in loop.pyx this behaves Badly
| #!/usr/bin/env python | |
| import threading, asyncio, time, multiprocessing | |
| import loop | |
| def threadmain(count: int = 100, kind: str = 'thread'): | |
| for i in range(count): | |
| time.sleep(0.01) | |
| if i % 10 == 0: | |
| print(kind, i) | |
| def cpu_sleep(): | |
| print('starting cpu_sleep') | |
| t0 = time.time() | |
| print('iters', loop.cpu_sleep(2000)) | |
| print('cpu_sleep', time.time() - t0) | |
| async def asyncmain(count: int = 100): | |
| # await asyncio.create_task(cpu_sleep()) | |
| for i in range(count): | |
| await asyncio.sleep(0.01) | |
| if i % 10 == 0: | |
| print('async', i) | |
| def main(): | |
| tasks = [ | |
| threading.Thread(target=threadmain), | |
| multiprocessing.Process(target=threadmain, kwargs={'kind': 'proc'}), | |
| threading.Thread(target=cpu_sleep), | |
| threading.Thread(target=cpu_sleep), | |
| ] | |
| for t in tasks: t.start() | |
| asyncio.run(asyncmain()) | |
| for t in tasks: t.join() | |
| print('ok joins') | |
| if __name__ == '__main__': | |
| main() |
| import cython | |
| from posix.time cimport timespec, clock_gettime, CLOCK_REALTIME, time_t | |
| cdef int to_millis(timespec* t) nogil: | |
| return (t.tv_sec * 1000) + (t.tv_nsec // 1000000) | |
| cdef int diff(timespec* a, timespec* b) nogil: | |
| "return diff in milliseconds" | |
| return to_millis(b) - to_millis(a) | |
| cdef int _cpu_sleep(int milliseconds) nogil: | |
| cdef timespec base, latest; | |
| clock_gettime(CLOCK_REALTIME, &base) | |
| clock_gettime(CLOCK_REALTIME, &latest) | |
| cdef unsigned int iter = 0 | |
| cdef int elapsed = diff(&base, &latest) | |
| cdef int ratchet = 0 | |
| while elapsed < milliseconds: | |
| iter += 1 | |
| clock_gettime(CLOCK_REALTIME, &latest) | |
| elapsed = diff(&base, &latest) | |
| if elapsed > ratchet + 100: | |
| ratchet += 100 | |
| # print('elapsed', ratchet) | |
| return iter | |
| def cpu_sleep(int milliseconds): | |
| cdef int ret | |
| # without nogil, this blocks python | |
| with nogil: | |
| ret = _cpu_sleep(milliseconds) | |
| return ret |
| loop.cpython-310-x86_64-linux-gnu.so: loop.pyx | |
| cythonize -i $^ |