-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Parallel transaction execution #2919
Changes from all commits
7570cec
0aceab8
503a7cd
b594c71
1419c72
05812ee
24e97c1
5e00b12
97303f9
d559d99
b4d08d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,8 @@ | |
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Neo.Ledger | ||
{ | ||
|
@@ -414,26 +416,47 @@ private void Persist(Block block) | |
all_application_executed.Add(application_executed); | ||
transactionStates = engine.GetState<TransactionState[]>(); | ||
} | ||
DataCache clonedSnapshot = snapshot.CreateSnapshot(); | ||
// Warning: Do not write into variable snapshot directly. Write into variable clonedSnapshot and commit instead. | ||
|
||
var tasks = new List<Task<(TransactionState, DataCache, ApplicationExecuted)>>(); | ||
|
||
// Warning: Do not write into variable snapshot directly. | ||
// Write into variable clonedSnapshot and commit instead. | ||
foreach (TransactionState transactionState in transactionStates) | ||
{ | ||
Transaction tx = transactionState.Transaction; | ||
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, system.Settings, tx.SystemFee); | ||
engine.LoadScript(tx.Script); | ||
transactionState.State = engine.Execute(); | ||
if (transactionState.State == VMState.HALT) | ||
DataCache txSnapshot = snapshot.CreateSnapshot(); | ||
var task = OnExecuteTransactionAsync(system, block, txSnapshot, transactionState); | ||
tasks.Add(task); | ||
} | ||
|
||
// var readSet = new HashSet<StorageKey>(); | ||
var writeSet = new HashSet<StorageKey>(); | ||
|
||
var randomUsed = false; | ||
foreach (var task in tasks) | ||
{ | ||
var (transactionState, txSnapshot, executed) = task.Result; | ||
|
||
if (txSnapshot.GetReadSet().Overlaps(writeSet) || (randomUsed && txSnapshot.isRandomNumberCalled)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if I have tx A reading nothing and writing into K some V1 and tx B reading nothing and writing into K some V2? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then tx B will reexecute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will, this is a feature anyway, for potential ecosystem boost~. I know this is useful only if we push to the neo limit. In reality, of not much use considering those 0 tx blocks. |
||
{ | ||
clonedSnapshot.Commit(); | ||
DataCache tmp = snapshot.CreateSnapshot(); | ||
var task2 = OnExecuteTransactionAsync(system, block, tmp, transactionState); | ||
tasks.Add(task2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why adding to the list if you're waiting for the result below anyway? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. parallel execution, sequential commit. In this way, we can start processing commit in the original order on txs that are alrady finished without waiting for the whole execution to finish. |
||
(transactionState, txSnapshot, executed) = task.Result; | ||
} | ||
else | ||
|
||
if (transactionState.State == VMState.HALT) | ||
{ | ||
clonedSnapshot = snapshot.CreateSnapshot(); | ||
txSnapshot.Commit(); | ||
writeSet.UnionWith(txSnapshot.GetWriteSet()); | ||
randomUsed = txSnapshot.isRandomNumberCalled; | ||
} | ||
ApplicationExecuted application_executed = new(engine); | ||
Context.System.EventStream.Publish(application_executed); | ||
all_application_executed.Add(application_executed); | ||
|
||
// ApplicationExecuted application_executed = new(engine); | ||
Context.System.EventStream.Publish(executed); | ||
all_application_executed.Add(executed); | ||
|
||
} | ||
|
||
using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block, system.Settings, 0)) | ||
{ | ||
engine.LoadScript(postPersistScript); | ||
|
@@ -491,6 +514,23 @@ private static ImmutableHashSet<UInt160> UpdateExtensibleWitnessWhiteList(Protoc | |
} | ||
return builder.ToImmutable(); | ||
} | ||
|
||
private static async Task<(TransactionState, DataCache, ApplicationExecuted)> OnExecuteTransactionAsync(NeoSystem system, Block block, DataCache clonedSnapshot, TransactionState transactionState) | ||
{ | ||
ApplicationExecuted application_executed = null; | ||
|
||
await Task.Run(() => | ||
{ | ||
Transaction tx = transactionState.Transaction; | ||
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, system.Settings, tx.SystemFee); | ||
engine.LoadScript(tx.Script); | ||
transactionState.State = engine.Execute(); | ||
application_executed = new(engine); | ||
}); | ||
|
||
return (transactionState, clonedSnapshot, application_executed); | ||
|
||
} | ||
} | ||
|
||
internal class BlockchainMailbox : PriorityMailbox | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you combine these two loops? I see you using
OnExecuteTransactionAsync
to do the loading. But I think if you were to batch or even just have a callback method forOnExecuteTransactionAsync
you should be good and maybe speed up some more with one loop instead of two loops. Or you could useTask.WhenAll
to keep sync.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are different, parallel execution, sequential commit.