-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[RFC] Threading #1831
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
Comments
I think your idea of making a new vm per-thread is a good idea, we could probably clone the |
This is pretty interesting/exciting, I think I might work on preparing the vm for this. |
Well, just with this change: diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs
index f0265d1c..929ffab6 100644
--- a/vm/src/pyobject.rs
+++ b/vm/src/pyobject.rs
@@ -1177,11 +1177,11 @@ pub trait PyValue: fmt::Debug + Sized + 'static {
}
}
-pub trait PyObjectPayload: Any + fmt::Debug + 'static {
+pub trait PyObjectPayload: Send + Sync + Any + fmt::Debug + 'static {
fn as_any(&self) -> &dyn Any;
}
-impl<T: PyValue + 'static> PyObjectPayload for T {
+impl<T: Send + Sync + PyValue + 'static> PyObjectPayload for T {
#[inline]
fn as_any(&self) -> &dyn Any {
self cargo gives 716 errors in the codebase, so I think this will have to be a gradual process of converting our types to be |
Yes. This is going to take some time converting all objects to be // Temporary trait to follow the progress of threading conversion
pub trait ThreadSafe: Send + Sync {} And we will implement the |
Unfortunately, that won't necessarily work, since a lot of the built-in types store other |
The problem is that this is a circular dependency. I would like to be able to work on this in small steps. We can go in two ways here:
What do you think? Any other suggestions? |
After looking more into this my suggestion would not actually work as unsafe impl<T: PyValue> Send for PyObject<T> {}
unsafe impl<T: PyValue> Sync for PyObject<T> {} This should not affect the current implementation as we do not support threading yet but will allow us to change most object to |
Technically that would be unsound, but as its only temporary and we wouldn't actually be using it I think it might be fine. Also, have you tried adding just a |
Dang, just ran into a problem with using |
Wow that was quick 😅 |
Guys, I would like to help in supporting threading in RustPython without GIL !! |
Hi @redradist, That would be great. We have a lot of work to do!!! |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@redradist if you have any more problems with running on windows, could you open a separate issue for it? |
@coolreader18 @palaviv |
Sorry @redradist I have already finished that. There are many other things we need help with. Please look at the contribution guide. |
I think we can close this issue. RustPython supports threads without the GIL. You can test this with this example: # single_threaded.py
import time
from threading import Thread
COUNT = 500000
def countdown(n):
while n>0:
n -= 1
start = time.time()
countdown(COUNT)
end = time.time()
print('Time taken in seconds -', end - start) # multi_threaded.py
import time
from threading import Thread
COUNT = 500000
def countdown(n):
while n>0:
n -= 1
t1 = Thread(target=countdown, args=(COUNT//2,))
t2 = Thread(target=countdown, args=(COUNT//2,))
start = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end = time.time()
print('Time taken in seconds -', end - start) When running with CPython I get: > python3 single_threaded.py
Time taken in seconds - 0.023739099502563477
> python3 multi_threaded.py
Time taken in seconds - 0.023739099502563477 While in RustPython: > target/release/rustpython single_threaded.py
Time taken in seconds - 2.0910146236419678
> target/release/rustpython multi_threaded.py
Time taken in seconds - 1.4379611015319824 As you can see we are slow but we are slow on multiple threads concurrently 😄 |
@palaviv |
@redradist we currently don't support this but that sounds like a nice feature if you want to try and take it. |
No, I've just would like to see benchmarks before and after ... ) |
Summary
I think we have got to the stage we should support threading in RustPython.
Detailed Explanation
There was already some discussion on threading but I wanted to start a dedicated issue as it is a big decision. I will try to break the issue in order to make some order:
Definition of done
We need a user to be able to create a new thread in python and execute code on the same Python object from multiple threads:
GIL
I think that one of the biggest discussion is the GIL. Should we add a GIL to RustPython? Should we allow only one thread to use each object at a time?
Suggested approach
I suggest the following changes in order to reach the ability to spawn a new thread:
!sync
and!Send
.Arc
instead ofRc
inPyObjectRef
.pub type PyObjectRef = Arc<PyObject<dyn PyObjectPayload>>
PyValue
andPyObjectPayload
traits will implementSync
andSend
. Thus forcing us to make all Py* structs sync as well.Rc
,Cell
andRefCell
use to thread safe options. For exampleArc
,Mutex
andAtomic*
.Mutex
we will lock the mutex for our use. This will require careful handling when using internal objects to avoid deadlocks when used between threads.A simple example of the
start_new_thread
method in_thread
will look something like:Drawbacks, Rationale, and Alternatives
I suggest that we will ignore the GIL and allow multiple threads to execute python code simultaneously. This can be a big advantage of RustPython on CPython. The user will be expected to make his own code thread safe. This might prove problematic to code that rely on the GIL.
What about third party library's? We do not yet have an API but would we force them to implement
Sync
andSend
as well?There was a lot of talk about alternatives and I cannot find all so please fill free to add in the comments. One alternatives that was suggested is Crossbream.
Unresolved Questions
The text was updated successfully, but these errors were encountered: