Skip to content
This repository has been archived by the owner on Nov 22, 2023. It is now read-only.

Optimize memory limits #230

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions src/neo-vm/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace Neo.VM
{
[DebuggerDisplay("RVCount={RVCount}, InstructionPointer={InstructionPointer}")]
public sealed class ExecutionContext
public sealed class ExecutionContext : IMemoryItem
{
private readonly Dictionary<Type, object> states = new Dictionary<Type, object>();

Expand Down Expand Up @@ -65,8 +65,9 @@ public Instruction NextInstruction
/// <param name="script">Script</param>
/// <param name="callingScript">The calling script</param>
/// <param name="rvcount">Number of items to be returned</param>
internal ExecutionContext(Script script, Script callingScript, int rvcount)
: this(script, callingScript, rvcount, new RandomAccessStack<StackItem>(), new RandomAccessStack<StackItem>())
/// <param name="memory">Memory</param>
internal ExecutionContext(Script script, Script callingScript, int rvcount, ReservedMemory memory)
: this(script, callingScript, rvcount, new RandomAccessStack<StackItem>(memory), new RandomAccessStack<StackItem>(memory))
{
}

Expand All @@ -79,6 +80,18 @@ private ExecutionContext(Script script, Script callingScript, int rvcount, Rando
this.CallingScript = callingScript;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnAddMemory(ReservedMemory memory)
{
memory.AllocateMemory();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnRemoveFromMemory(ReservedMemory memory)
{
memory.FreeMemory();
}

internal ExecutionContext Clone()
{
return new ExecutionContext(Script, Script, 0, EvaluationStack, AltStack);
Expand Down
185 changes: 26 additions & 159 deletions src/neo-vm/ExecutionEngine.cs

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/neo-vm/IMemoryItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Neo.VM
{
public interface IMemoryItem
{
/// <summary>
/// Is invoked when the item will be added to the memory
/// </summary>
/// <param name="memory">Memory</param>
void OnAddMemory(ReservedMemory memory);

/// <summary>
/// Is invoked when the item will be removed from the memory
/// </summary>
/// <param name="memory">Memory</param>
void OnRemoveFromMemory(ReservedMemory memory);
}
}
36 changes: 33 additions & 3 deletions src/neo-vm/RandomAccessStack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,43 @@ namespace Neo.VM
{
[DebuggerDisplay("Count={Count}")]
public class RandomAccessStack<T> : IReadOnlyCollection<T>
where T : IMemoryItem
{
private readonly List<T> list = new List<T>();
private readonly ReservedMemory _memory;

public int Count => list.Count;
public RandomAccessStack(ReservedMemory memory)
{
_memory = memory;
}

public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return list.Count; }
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_memory.RemoveRange(list);
list.Clear();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(RandomAccessStack<T> stack, int count = -1)
{
if (count == 0) return;
if (count == -1)
{
stack._memory.AddRange(list);
stack.list.AddRange(list);
}
else
{
stack._memory.AddRange(list.Skip(list.Count - count));
stack.list.AddRange(list.Skip(list.Count - count));
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -41,12 +60,15 @@ IEnumerator IEnumerable.GetEnumerator()
return list.GetEnumerator();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Insert(int index, T item)
{
if (index > list.Count) throw new InvalidOperationException();
_memory.Add(item);
list.Insert(list.Count - index, item);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Peek(int index = 0)
{
if (index >= list.Count) throw new InvalidOperationException();
Expand All @@ -55,7 +77,7 @@ public T Peek(int index = 0)
index += list.Count;
if (index < 0) throw new InvalidOperationException();
}
return list[(list.Count - index - 1)];
return list[list.Count - index - 1];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -73,6 +95,7 @@ public bool TryPop(out T item)
{
item = list[index];
list.RemoveAt(index);
_memory.Remove(item);
return true;
}

Expand All @@ -89,6 +112,7 @@ public bool TryPop<TItem>(out TItem item) where TItem : T
{
item = i;
list.RemoveAt(index);
_memory.Remove(i);
return true;
}

Expand All @@ -99,9 +123,11 @@ public bool TryPop<TItem>(out TItem item) where TItem : T
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Push(T item)
{
_memory.Add(item);
list.Add(item);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Remove(int index)
{
if (index >= list.Count) throw new InvalidOperationException();
Expand All @@ -113,9 +139,11 @@ public T Remove(int index)
index = list.Count - index - 1;
T item = list[index];
list.RemoveAt(index);
_memory.Remove(item);
return item;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int index, T item)
{
if (index >= list.Count) throw new InvalidOperationException();
Expand All @@ -124,7 +152,9 @@ public void Set(int index, T item)
index += list.Count;
if (index < 0) throw new InvalidOperationException();
}
list[(list.Count - index - 1)] = item;
_memory.Remove(list[list.Count - index - 1]);
_memory.Add(item);
list[list.Count - index - 1] = item;
}
}
}
141 changes: 141 additions & 0 deletions src/neo-vm/ReservedMemory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Neo.VM
{
[DebuggerDisplay("Reserved={Reserved}, Used={Used}, Free={Free}")]
public class ReservedMemory
{
class ReferenceEqualsComparer : IEqualityComparer<IMemoryItem>
{
public bool Equals(IMemoryItem x, IMemoryItem y) => ReferenceEquals(x, y);
public int GetHashCode(IMemoryItem obj) => RuntimeHelpers.GetHashCode(obj);
}

private static readonly ReferenceEqualsComparer _comparer = new ReferenceEqualsComparer();

/// <summary>
/// These entries contains the item and the memory counter
/// </summary>
private readonly IDictionary<IMemoryItem, int> _entries = new Dictionary<IMemoryItem, int>(_comparer);

/// <summary>
/// Free memory
/// </summary>
public int Free => Reserved - Used;

/// <summary>
/// Used
/// </summary>
public int Used { get; private set; }

/// <summary>
/// Reserved
/// </summary>
public int Reserved { get; internal set; }

/// <summary>
/// Constructor
/// </summary>
/// <param name="reserved">Reserved</param>
public ReservedMemory(int reserved = 1024)
{
Reserved = reserved;
}

/// <summary>
/// Allocate memory
/// </summary>
/// <param name="memory">Memory</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AllocateMemory(int memory = 1)
{
Used = checked(Used + memory);

if (Used > Reserved)
{
throw new OutOfMemoryException();
}
}

/// <summary>
/// Free memory
/// </summary>
/// <param name="memory">Memory</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void FreeMemory(int memory = 1)
{
Used = checked(Used - memory);
}

/// <summary>
/// Add item
/// </summary>
/// <param name="item">Item</param>
public void Add(IMemoryItem item)
{
if (_entries.TryGetValue(item, out var actual))
{
// Increase the counter in one

_entries[item] = actual + 1;
}
else
{
_entries.Add(item, 1);
item.OnAddMemory(this);
}
}

/// <summary>
/// Remove
/// </summary>
/// <param name="item">Item</param>
public void Remove(IMemoryItem item)
{
if (_entries.TryGetValue(item, out var actual))
{
if (actual <= 1)
{
_entries.Remove(item);
item.OnRemoveFromMemory(this);
}
else
{
// Decrease the counter in one

_entries[item] = actual - 1;
}
}
else
{
// This never should happend
throw new ArgumentException();
}
}

/// <summary>
/// Add range
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="items">Items</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddRange<T>(IEnumerable<T> items) where T : IMemoryItem
{
foreach (var item in items) Add(item);
}

/// <summary>
/// Remove Range
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="items">Items</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveRange<T>(IEnumerable<T> items) where T : IMemoryItem
{
foreach (var item in items) Remove(item);
}
}
}
Loading