Skip to content

Commit

Permalink
Share stack pool between threads (#7972)
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams authored Dec 30, 2024
1 parent 4ca86b9 commit 2cda94d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 59 deletions.
66 changes: 7 additions & 59 deletions src/Nethermind/Nethermind.Evm/EvmState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Nethermind.Core;
using Nethermind.State;

Expand All @@ -16,57 +14,7 @@ namespace Nethermind.Evm
[DebuggerDisplay("{ExecutionType} to {Env.ExecutingAccount}, G {GasAvailable} R {Refund} PC {ProgramCounter} OUT {OutputDestination}:{OutputLength}")]
public class EvmState : IDisposable // TODO: rename to CallState
{
private class StackPool
{
private readonly int _maxCallStackDepth;
private readonly struct StackItem(byte[] dataStack, int[] returnStack)
{
public readonly byte[] DataStack = dataStack;
public readonly int[] ReturnStack = returnStack;
}

// TODO: we have wrong call depth calculation somewhere
public StackPool(int maxCallStackDepth = VirtualMachine.MaxCallDepth * 2)
{
_maxCallStackDepth = maxCallStackDepth;
}

private readonly Stack<StackItem> _stackPool = new(32);

private int _stackPoolDepth;

/// <summary>
/// The word 'return' acts here once as a verb 'to return stack to the pool' and once as a part of the
/// compound noun 'return stack' which is a stack of subroutine return values.
/// </summary>
/// <param name="dataStack"></param>
/// <param name="returnStack"></param>
public void ReturnStacks(byte[] dataStack, int[] returnStack)
{
_stackPool.Push(new(dataStack, returnStack));
}

public (byte[], int[]) RentStacks()
{
if (_stackPool.TryPop(out StackItem result))
{
return (result.DataStack, result.ReturnStack);
}

_stackPoolDepth++;
if (_stackPoolDepth > _maxCallStackDepth)
{
EvmStack.ThrowEvmStackOverflowException();
}

return
(
new byte[(EvmStack.MaxStackSize + EvmStack.RegisterLength) * 32],
new int[EvmStack.ReturnStackSize]
);
}
}
private static readonly ThreadLocal<StackPool> _stackPool = new(static () => new StackPool());
private static readonly StackPool _stackPool = new();

public byte[]? DataStack;

Expand All @@ -81,7 +29,7 @@ public void ReturnStacks(byte[] dataStack, int[] returnStack)
public int ReturnStackHead = 0;
private bool _canRestore = true;
/// <summary>
/// Contructor for a top level <see cref="EvmState"/>.
/// Constructor for a top level <see cref="EvmState"/>.
/// </summary>
public EvmState(
long gasAvailable,
Expand All @@ -101,7 +49,7 @@ public EvmState(
{
}
/// <summary>
/// Contructor for a top level <see cref="EvmState"/>.
/// Constructor for a top level <see cref="EvmState"/>.
/// </summary>
public EvmState(
long gasAvailable,
Expand All @@ -120,7 +68,7 @@ public EvmState(
{
}
/// <summary>
/// Contructor for a frame <see cref="EvmState"/> beneath top level.
/// Constructor for a frame <see cref="EvmState"/> beneath top level.
/// </summary>
internal EvmState(
long gasAvailable,
Expand Down Expand Up @@ -215,7 +163,7 @@ public void Dispose()
if (DataStack is not null)
{
// Only Dispose once
_stackPool.Value.ReturnStacks(DataStack, ReturnStack!);
_stackPool.ReturnStacks(DataStack, ReturnStack!);
DataStack = null;
ReturnStack = null;
}
Expand All @@ -228,14 +176,14 @@ public void InitStacks()
{
if (DataStack is null)
{
(DataStack, ReturnStack) = _stackPool.Value.RentStacks();
(DataStack, ReturnStack) = _stackPool.RentStacks();
}
}

public void CommitToParent(EvmState parentState)
{
parentState.Refund += Refund;
_canRestore = false; // we can't restore if we commited
_canRestore = false; // we can't restore if we committed
}

private void Restore()
Expand Down
48 changes: 48 additions & 0 deletions src/Nethermind/Nethermind.Evm/StackPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Concurrent;

namespace Nethermind.Evm;

internal class StackPool
{
// Also have parallel prewarming and Rpc calls
private const int MaxStacksPooled = VirtualMachine.MaxCallDepth * 2;
private readonly struct StackItem(byte[] dataStack, int[] returnStack)
{
public readonly byte[] DataStack = dataStack;
public readonly int[] ReturnStack = returnStack;
}

private readonly ConcurrentQueue<StackItem> _stackPool = new();

/// <summary>
/// The word 'return' acts here once as a verb 'to return stack to the pool' and once as a part of the
/// compound noun 'return stack' which is a stack of subroutine return values.
/// </summary>
/// <param name="dataStack"></param>
/// <param name="returnStack"></param>
public void ReturnStacks(byte[] dataStack, int[] returnStack)
{
if (_stackPool.Count <= MaxStacksPooled)
{
_stackPool.Enqueue(new(dataStack, returnStack));
}
}

public (byte[], int[]) RentStacks()
{
if (_stackPool.TryDequeue(out StackItem result))
{
return (result.DataStack, result.ReturnStack);
}

return
(
new byte[(EvmStack.MaxStackSize + EvmStack.RegisterLength) * 32],
new int[EvmStack.ReturnStackSize]
);
}
}

0 comments on commit 2cda94d

Please sign in to comment.