MNT: Mark all `nogil` functions as `noexcept`
State: closed

- [ ] closes #xxxx (Replace xxxx with the GitHub issue number)
- [ ] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [ ] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [ ] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [ ] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.

This PR marks all `nogil` functions as `noexcept` because:
1. Currently, they are not propagating exception with Cython 0.29.X
2. I found out that several of functions are still checking exception after every call - e.g. https://github.com/pandas-dev/pandas/blob/75daea460b3c2f14ece2c7e10d34ce90b0e52f8a/pandas/_libs/tslibs/ccalendar.pyx#L158
3. Several functions returning `void` were missed in #53144

> **Note**: `nogil` function calls checking the exception after every function call is introducing non-negligible performance degradation - see https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#error-return-values

Labels:
name: Internals
description: Related to non-user accessible pandas implementation

Comments:
@h-vetinari could you run tests over this PR using Cython3.0 b3? Unittests are failing on my computer but I am not 100% convinced that it is caused by changes in this PR.
> @h-vetinari could you run tests over this PR using Cython3.0 b3?

Running in https://github.com/conda-forge/pandas-feedstock/pull/162
Ah, I should have said: I'm running the changes of this PR on top of 2.0.1. If we need everything that's in main as well, then I'll have to do it differently...
So, on top of 2.0.1, the only failure on unix is `test_add_out_of_pydatetime_range`, but that was pre-existing. Windows has three other failures, but those also were pre-existing.
What does something being nogil have to do with its error handling? I *think* this is OK but not sure I follow the reasoning in the OP; want to make sure we don't lose exception handling we actually want
> What does something being nogil have to do with its error handling?

It has nothing to do with nogil, but with Cython 3 now [propagating](https://github.com/cython/cython/issues/4280) exceptions by default, and it being beneficial to mark those functions that will not raise as `noexcept` for performance reasons.
Cool makes sense and thanks for the info. This lgtm - assuming we can look at non-nogil functions in a follow up?
> What does something being nogil have to do with its error handling?

Let me explain it using following function from PR: https://github.com/pandas-dev/pandas/blob/8cf4ab491b06274f555d1ea305c172354208747a/pandas/_libs/window/aggregations.pyx#L115-L116

1. The function has `void` return type which in Cython3 means that by default **after every call the exception is checked**. This single point is introducing a small (maybe negligible) performance hit. In Cython 0.29.X you did not care since the default behaviour is to not propagate exception information.
2. But the `gil` keyword is making it worse when function is called within `with nogil` block - e.g. here:
https://github.com/pandas-dev/pandas/blob/8cf4ab491b06274f555d1ea305c172354208747a/pandas/_libs/window/aggregations.pyx#L167
In this case cython3 generates following code:
   ```c
    __pyx_f_6pandas_5_libs_6window_12aggregations_remove_sum(/*function args*/); if (unlikely(__Pyx_ErrOccurredWithGIL())) __PYX_ERR(0, 167, __pyx_L4_error)
   ```
   The problem is caused by `__Pyx_ErrOccurredWithGIL()` which acquires GIL lock. This can hit performance seriously. See https://github.com/cython/cython/issues/5324 for details.

 
Thanks @matusvalo