Skip to content

Instantly share code, notes, and snippets.

@abe-winter
Created August 28, 2023 14:55
Show Gist options
  • Save abe-winter/23271d3ca24d57f0a9d754f22dcfd464 to your computer and use it in GitHub Desktop.
Save abe-winter/23271d3ca24d57f0a9d754f22dcfd464 to your computer and use it in GitHub Desktop.
heartbeat example

heartbeat example

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 $^
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment