-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Runtime worker threads #1459
Comments
Can we simply not expose the functions that write storage to the worker, and require that the worker have a separate wasm blob a la WebWorkers? If we did that, attempts to write to storage from a worker would be caught at load-time. |
Sweet; as soon as I am back (Jan): finish offcahin phragmen and get started with this. Looking forward 👍 |
Some more additional notes from the latest company retreat, in which we briefly discussed this:
The memory model gurantee that we can promise in the image above is same as: https://www.cs.utexas.edu/~bornholt/post/memory-models.html.
|
I wrote these notes from our discussion at the coding retreat: We discussed 2.5 possible memory models for parallel transaction evaluation at Gavin's session on parallelization today. Gavin's original suggestion:
We believe this approach corresponds to one of the weaker eventual consistency memory models used in multi-threaded execution. https://www.cs.utexas.edu/~bornholt/post/memory-models.html In particular, you could easily make smart contracts whose sequential semantics becomes insecure under this memory model. Jeff proposed we maintain full sequential consistency:
We think this should be equivalent to the more "rust-like" model in which the spawn thread commands reserve portions of the state tree as mutable or immutable. At first blush, I'd think this reservation model runs faster but increases block size unacceptably. We should support both these memory models in the code, and make smart contract platforms like ink use the full sequential consistency model, but attempt to prove much of the relay chain itself can run in the weaker model.
After we have some designs like ICMP designed for the weaker model then we should use good auditors, like maybe Ralf Jung from the RustBelt project. |
@burdges what does ICMP stand for? I only know of Internet Control Message Protocol, but that makes no sense here. |
inter chain message passing, afaik |
We've renamed it to XCMP for precisely that reason |
As discussed this changes the memory model; And more importantly, wouldn't it be a huge problem for Kusama? previous blocks, when executed with sequential consistency might lead to different outcomes once executed with a runtime workers. |
How is How should it affect kusama? Even if this would be work out of the box (without changing the runtime, what I don't expect), why should the storage root be different? If the storage root would be different, it would mean that there were multiple transactions modifying the same memory position. This would be a violation anyway. I have some further questions: |
If you buffer you basically go from Sequential (SC) to TSO model. After a write, your thread will have access to an updated version whilst others will only ever see the initial state of the block, regardless of any schedule. The link above is a good read about it.
You can get a different root even if there is just a read-write conflict. In a previous block, key if you re-execute the same block with a runtime worker,
What do we consider a violation currently in our pure sequential model? I infer from your comment that two writes to the same key within a block is a violation? I don't see why it should be, given that everyone does the same thing deterministically.
Yeah indeed first block production. Import would come next and would be mostly about the follow up of this issue:
Once an author can distribute a schedule, the role of the import would be simply to use it and import in parallel. That's in the future though. |
Another fishy example that came to my mind: If we are to allow multiple transactions from the same origin in the same block, then the fee updates need to be precisely NOT buffered, otherwise one who submits Maybe we should also provide some memory fence to ensure such operations are actually visible to all other threads before we can move on. |
Agreed, we cannot process multiple transactions spending from the same source in separate threads. All memory models discussed above should handle that particular case, but these memory models differ when some read the account and only one spends from the account. In the short term, we might simply forbid block from containing multiple transactions that require signatures by the same account. |
I'll add sort of |
I don't understand this example :D
Okay, if we expect pure writes. Like someone for whatever reasons writes to a certain location some value that has no other connection, your view is correct. However, I think that this doesn't exist in a real world scenario. Let us for example just take the current weight of the block, that is a field that is read and written by a transaction. We need to synchronize these reads and writes, as otherwise the value will just be garbage. Or think of a transaction where people want to put some money in a pot. Two different people want to do this in one block. Both transactions would need to happen in the same thread, as otherwise you would have a violation.
I thought about this again and think that maybe doing import is easier and not that hard. You probably just need some sort of heuristic to distribute the tx between threads and start applying them. The good, if the state root is different you can just do single threaded import. For block production you probably not have enough time to switch again to the single threaded strategy. |
Speaking as a potential user of a blockchain, I don’t consider that acceptable. The problem is that I might submit a transaction, and then decide I want to submit yet another transaction. Artificially delaying the second one doesn’t seem like a good idea. |
Also, sequential consistency is not enough. What we need is determinism. The best solution I can come up with is software transactional memory, or STM. If we have a conflict, one of the transactions is rolled back and retried later. Obviously, the order in which transactions appear to have been executed needs to be in the block. |
Updating that due to the workload from the staking side and my sightly limited availability int he next few months, I see it very unlikely that I can work on this. |
Remove DSN online status and increase get-piece backoff time.
The runtime is currently single threaded, which means all transactions/extrinsics must be executed in series. This is rather suboptimal. Instead, worker "threads" should be allowed in the runtime environment.
Since this is a consensus computation and the need for determinism is absolute, there should be a number of restrictions on worker threads:
They should be pure functions. This implies that as long as there is at least one worker thread, storage may not be altered (though it may be read). Furthermore, there should be no other kinds of data-sharing between threads, even within
Sync
safeguards likeMutex
orAtomic
. Separate memory slabs should be used if needed.Worker threads may only be created from the main thread and have a specific identifier that is deterministic based upon the order in which they were created by the main thread.
If, while there is at least one worker thread, storage is written to, then there are two options that we should do one of:
set_storage
buffer the alteration in a (thread-local) hash map, which ultimately takes effect once all worker threads are finished and in the order that worker threads were created (and thus deterministic).Once this exists, a new issue should be created to introduce extrinsic parallelism whereby the block author prefixes the extrinsic set with an extrinsic to specify a dependency graph stating which extrinsics are mutually independent and can be executed in parallel. The runtime
executive
can then use this information together with a number of worker threads in order to safely parallelise extrinsic queue execution.The text was updated successfully, but these errors were encountered: