-
Notifications
You must be signed in to change notification settings - Fork 758
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python::allow_threads() causes segmentation fault #649
Comments
This function is not intended to execute Python functions in parallel. In your example, you call But...
(from your comment in #640) |
It seems that implementing the format traits for Py structs is a bad idea, since they internally call Python code without ensuring the GIL being held. A workaround could be a wrapper including the GIL proof, something like
|
@sebpuetz
That's right, but
|
Well, simple: if it can segfault or otherwise circumvent Python API requirements from safe code, it has to go. If PyO3 doesn't want to commit to this, it won't be accepted by the Rust community. |
Yeah I agree, being able to debug-print Python objects on-the-fly is really useful. It wouldn't be the end of the world if we had to pass the But if there are other functions that access the Python interpreter internally, then we need a more general solution, because they would also blow up when called from within |
@sebpuetz Here's an example : Rust: use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[pymodule]
fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
#[pyfunction]
fn expensive_function(py: Python, init: usize) -> usize {
(0..10000000).fold(init, |acc, v| acc + v)
}
m.add_wrapped(wrap_pyfunction!(expensive_function))?;
Ok(())
} Python : import time
from concurrent.futures import ThreadPoolExecutor
from my_module import expensive_function
executor = ThreadPoolExecutor(max_workers=2)
t0 = time.time()
future_1 = executor.submit(expensive_function, 100)
future_2 = executor.submit(expensive_function, 200)
result_1 = future_1.result()
result_2 = future_2.result()
t1 = time.time()
print(f"Time: {t1 - t0}s") In that example, the Python side spawns 2 separate threads to execute the function, but because the GIL stays locked when executing the Rust code, they cannot run in parallel.
Now if we wrap the execution in #[pyfunction]
fn expensive_function(py: Python, init: usize) -> usize {
py.allow_threads(|| {
(0..10000000).fold(init, |acc, v| acc + v)
})
} Suddenly it's twice as fast :
This is because The documentation at https://github.com/PyO3/pyo3/blob/master/guide/src/parallelism.md is confusing because it suggests that you need to release the GIL to achieve true parallelism within your Rust code (with your own internal threads), which isn't true. AFAIK you only need it if you have multi-threading on the Python side. |
@birkenfeld @Askannz
I checked CPython codes. |
Hmm, but if |
I made them unsendable in #655 |
Closed via #655 |
🐛 Bug Reports
Accessing some Python objects from within the closure given to
Python::allow_threads()
causes a segmentation fault. I can reproduce this behavior withPyList
,PyTuple
, andPyArray
from thenumpy
crate (haven't tested with other objects).🌍 Environment
rustc --version
): rustc 1.39.0-nightly (ca3766e2e 2019-09-14)version = "0.x.y"
withgit = "https://github.com/PyO3/pyo3")?
Yes💥 Reproducing
Rust code:
Python shell:
The text was updated successfully, but these errors were encountered: