-
Notifications
You must be signed in to change notification settings - Fork 597
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
Node should be using message-passing concurrency #121
Comments
Unless I missed something, network message handler is currently called protocol. |
Typo. Fixed. |
Some tasks for me and @azban :
|
Signer is a trait - it can be implemented with expensive implementation. @nearmax I'm unclear what you mean by pool of Runtimes Also where is the chains/client? |
s/Signer/InMemorySigner We are going to be executing multiple contracts concurrently. Currently the contracts are executed by Runtime. To execute them concurrently we would need a pool of workers that runs Runtimes. The reason why we cannot spawn them as separate tasks is because we want to control the number of threads they are running on and prevent them from affecting other async tasks, like the network listener. Chain is a heavy resource with frequent access requirements, we will wrap it into lock+arc for now. Regarding the
Therefore, the following functions that we are currently using to produce fake blocks are going to go away: |
We probably don't need a pool of runtimes for now, as doing so seems error-prone and requires more effort than we can afford. The main reason is that the order of transactions matter in a lot of cases and trying to separate them into independent tasks requires careful manipulation and also incurs some overhead on its own. I am also not entirely convinced that we need to spawn a network message handler whenever a new message comes in. There will be some shared state anyways (things like peer_info) and processing each message should take very little time on the protocol level. I guess we should do some experiments and see how much concurrency helps there. |
We cannot be computing the state one transaction at a time, it is too slow. I also disagree that the separation is hard to implement. It is a simple greedy system. TxFlow already defines for us the global order of the transactions which only matters for those transactions that touch same addresses. So at each step we look in the bucket of unprocessed transactions and take any transaction that does not have unprocessed dependencies. Otherwise we wait. Regarding having one handler per message. See the first two examples in tokio documentation: https://tokio.rs/docs/getting-started/echo/ . What we have currently is the first example. What I suggest is the second example. Even if protocol turns out to be lightweight we have need to spawn a separate task per message anyway to send the data down the channel, like here: https://github.com/nearprotocol/nearcore/blob/7f6ec43f8a9147d4bae80ff9859ec55fa7ccc516/core/txflow/src/txflow_task/mod.rs#L104 |
I think we can probably parallelize executions of cross-shard calls and same-shard calls without too much trouble. For calls in the same shard, it is much harder because one call could lead to some other calls in the same shard again and the current design is to execute them in the same block, which makes parallelizing them hard due to possibly complicated dependencies. Some static analysis could help, but I am not too sure about that. @ilblackdragon |
Yes, that's a special case. I think most of our transactions are not going to be like that. We can make a static analysis of the contract and establish what addresses it can potentially touch and then execute all contracts that are not affected by it independently in parallel. Also, for MVB we might consider dropping the guarantee to execute them in the same block for now. Also, remember that we are targeting 100k tps. With 1-4k shards it means each verifier needs to execute 25-100 contracts a second. The beacon chain witnesses will need to be executing even more! That means if we execute them sequentially it should be faster than 10-40ms, which is ridiculously fast. |
@nearmax let's implement single-threaded design first and make it work. I don't think we have time to handle additional complexity (like static analyzer) now. After it's working properly (but slowly) we'll measure whether anything needs to be sped up. Note that we don't want to over-utilize CPU anyway (running on mobiles) so shouldn't aim for 100% CPU load. |
Ok, let's have one worker and parallelize it later after we measure the load. |
In order to be more aligned with the interface of libp2p and other Rust libraries that use futures we need to replace some of our state-sharing concurrency with message-passing concurrency. Which would also allow us to get rid of some thread-safety primitives and hopefully be faster.
The following things should be run as tasks:
The text was updated successfully, but these errors were encountered: