You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
More jit options, specifically nogil=True on the serial kernels #213
Hi @flying-sheep I was wondering why the serial (fallback) kernels aren't compiled with nogil=True. This would enable parallelization via joblib and dask schedulers. Is there a reason for it? If not I can open a PR showing the speedup also.
I also want to point to something I learned. Tbb doesn't oversubscribe because it uses a process-wide arena. So, we can suggest tbb in certain OS'es instead of falling back. But setting nogil=True will be enough to be fast enough in itself.
nopython – maybe we should just switch to jit and set nopython=True as the default, as compat mode might come in handy
nogil – unclear what the default should be, please investigate if there are potential issues (maybe there are only potential issues with e.g. nopython=False or so)
error_model – might come in handy for functions where divisions by zero creating INF makes sense
inline & force_inline – help to modularize code without sacrificing performance
locals – idk if it has any advantage over just doing things like foo = np.int32(bar)
boundscheck – maybe good just for development: enable while working on the code, disable once all the unit tests exist
no:
forceobj – I don’t know why one would want this, but if someone can tell me, maybe it can be bumped up
cache & parallel – the whole point of our wrapper is to smartly dispatch both while having caching. If users don’t want this, they should just use stock jit/njit
PS: I didn’t immediately make a 1.5.1 release with #212. If this issue takes longer to complete, we should release 1.5.1 in the meantime, what do you think?
changed the title [-]Why isn't nogil=True on the serial kernels[/-][+]More `jit` options, specifically `nogil=True` on the serial kernels[/+]on Sep 18, 2026
Let me have a deep look into this. Normally nogil=True by default on serial would give quick wins for me in squidpy and plenty of reuse but I will have a look because it seems worth it. This is one prevents me of reusing honestly fau to it's full extend.
But I think we can go with a release now because I want to dig deep a bit about the side effects and stuff
nogil – unclear what the default should be, please investigate if there are potential issues (maybe there are only potential issues with e.g. nopython=False or so)
The only reason I haven't jumped at this in the past is because nogil means that thread safety is no longer guaranteed i.e., in a dask thread-based scheduler. Granted, you shouldn't be doing "edit" operations on shared buffers in dask in the first place, but that is the only risk. I would probably make a big fat changelog note, run downstream packages' test suites, and if no problems, let it rip!
I think I will make a simple PR changing the default of nogil. On the other parameters I have some questions about fau in general before even considering implementing them. Besides the fallback of parallel and serial, I don't get why we wrap the whole njit tbh. For example why don't we provide nogil_thread_safety_fallback as a wrapper that only consumes parallel=True and gives the rest to njit as is? If the user gave something incompatible, it would be numbas problem and we would be fulfilling our contract. I will create separate issues about this I think
The only reason I haven't jumped at this in the past is because nogil means that thread safety is no longer guaranteed i.e., in a dask thread-based schedule
@ilan-gold I agree that it's a behavior change and I will write a changelog but I wouldn't call it a contractual breaking change since the nogil=False and parallelize=False wouldn't guarantee thread safety as well. It would perhaps accidentally avoid data corruptio but a synchronization unaware algorithm is unsafe technically as long as there is context switching and threads in the OS.
In fact I made claude write this example, which by setting more frequent context switches would make the GIL locks shorter and would make it stop acting like an accidental mutex:
importsysimportthreadingimportnumbaimportnumpyasnpsys.setswitchinterval(1e-6) # force frequent handovers; the default 5 ms hides the race@numba.njit(nogil=False)defread(out):
returnout[0]
@numba.njit(nogil=False)defwrite(out, value):
out[0] =valueout=np.zeros(1, dtype=np.int64)
defbump():
for_inrange(20_000):
# the GIL is held *through* each call and released *between* them,# so the other thread slips in right here and both write the same valuewrite(out, read(out) +1)
ts= [threading.Thread(target=bump) for_inrange(2)]
fortints:
t.start()
fortints:
t.join()
print(out[0], "of", 40_000) # ~27000: a third of the updates lost, at nogil=False25813of4000027149of4000027050of40000
Activity
flying-sheep commentedon Sep 18, 2026
No good reason, I just ported all things as they were.
@ilan-gold and I discussed elsewhere that our njit wrapper should actually just expose all the knobs that make sense: https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#numba.jit
nopython– maybe we should just switch tojitand setnopython=Trueas the default, as compat mode might come in handynogil– unclear what the default should be, please investigate if there are potential issues (maybe there are only potential issues with e.g.nopython=Falseor so)error_model– might come in handy for functions where divisions by zero creating INF makes senseinline&force_inline– help to modularize code without sacrificing performancemaybe:
fastmath– has sharp edges, as 1. everything needs to be compatible (seefastmath=Trueis used in incompatible functions (breaks in numba 0.62.0rc1+) lmcinnes/umap#1216) and 2.locals– idk if it has any advantage over just doing things likefoo = np.int32(bar)boundscheck– maybe good just for development: enable while working on the code, disable once all the unit tests existno:
forceobj– I don’t know why one would want this, but if someone can tell me, maybe it can be bumped upcache¶llel– the whole point of our wrapper is to smartly dispatch both while having caching. If users don’t want this, they should just use stockjit/njitPS: I didn’t immediately make a 1.5.1 release with #212. If this issue takes longer to complete, we should release 1.5.1 in the meantime, what do you think?
[-]Why isn't nogil=True on the serial kernels[/-][+]More `jit` options, specifically `nogil=True` on the serial kernels[/+]selmanozleyen commentedon Sep 18, 2026
Let me have a deep look into this. Normally nogil=True by default on serial would give quick wins for me in squidpy and plenty of reuse but I will have a look because it seems worth it. This is one prevents me of reusing honestly fau to it's full extend.
But I think we can go with a release now because I want to dig deep a bit about the side effects and stuff
ilan-gold commentedon Sep 18, 2026
The only reason I haven't jumped at this in the past is because
nogilmeans that thread safety is no longer guaranteed i.e., in a dask thread-based scheduler. Granted, you shouldn't be doing "edit" operations on shared buffers in dask in the first place, but that is the only risk. I would probably make a big fat changelog note, run downstream packages' test suites, and if no problems, let it rip!selmanozleyen commentedon Sep 18, 2026
I think I will make a simple PR changing the default of nogil. On the other parameters I have some questions about fau in general before even considering implementing them. Besides the fallback of parallel and serial, I don't get why we wrap the whole njit tbh. For example why don't we provide nogil_thread_safety_fallback as a wrapper that only consumes parallel=True and gives the rest to njit as is? If the user gave something incompatible, it would be numbas problem and we would be fulfilling our contract. I will create separate issues about this I think
@ilan-gold I agree that it's a behavior change and I will write a changelog but I wouldn't call it a contractual breaking change since the nogil=False and parallelize=False wouldn't guarantee thread safety as well. It would perhaps accidentally avoid data corruptio but a synchronization unaware algorithm is unsafe technically as long as there is context switching and threads in the OS.
In fact I made claude write this example, which by setting more frequent context switches would make the GIL locks shorter and would make it stop acting like an accidental mutex: