Skip to content
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

Optimize Jump Destination Analysis #6502

Merged
merged 1 commit into from
Jan 12, 2024
Merged
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

This file was deleted.

53 changes: 8 additions & 45 deletions src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/CodeInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void Validate_CodeBitmap_With_Push30()
}

[Test]
public void Small_Jumpdest_Use_CodeDataAnalyzer()
public void Small_Jumpdest()
{
byte[] code =
{
Expand All @@ -116,15 +116,10 @@ public void Small_Jumpdest_Use_CodeDataAnalyzer()
CodeInfo codeInfo = new(code);

codeInfo.ValidateJump(10, false).Should().BeTrue();

FieldInfo field = typeof(CodeInfo).GetField(AnalyzerField, BindingFlags.Instance | BindingFlags.NonPublic);
var calc = field.GetValue(codeInfo);

Assert.IsInstanceOf<CodeDataAnalyzer>(calc);
}

[Test]
public void Small_Push1_Use_CodeDataAnalyzer()
public void Small_Push1()
{
byte[] code =
{
Expand All @@ -134,73 +129,41 @@ public void Small_Push1_Use_CodeDataAnalyzer()
CodeInfo codeInfo = new(code);

codeInfo.ValidateJump(10, false).Should().BeFalse();

FieldInfo field = typeof(CodeInfo).GetField(AnalyzerField, BindingFlags.Instance | BindingFlags.NonPublic);
var calc = field.GetValue(codeInfo);

Assert.IsInstanceOf<CodeDataAnalyzer>(calc);
}

[Test]
public void Jumpdest_Over10k_Use_JumpdestAnalyzer()
public void Jumpdest_Over10k()
{
var code = Enumerable.Repeat((byte)0x5b, 10_001).ToArray();

CodeInfo codeInfo = new(code);

codeInfo.ValidateJump(10, false).Should().BeTrue();

FieldInfo field = typeof(CodeInfo).GetField(AnalyzerField, BindingFlags.Instance | BindingFlags.NonPublic);
var calc = field.GetValue(codeInfo);

Assert.IsInstanceOf<CodeDataAnalyzer>(calc);
}

[Test]
public void Push1_Over10k_Use_JumpdestAnalyzer()
public void Push1_Over10k()
{
var code = Enumerable.Repeat((byte)0x60, 10_001).ToArray();

CodeInfo codeInfo = new(code);

codeInfo.ValidateJump(10, false).Should().BeFalse();

FieldInfo field = typeof(CodeInfo).GetField(AnalyzerField, BindingFlags.Instance | BindingFlags.NonPublic);
var calc = field.GetValue(codeInfo);

Assert.IsInstanceOf<JumpdestAnalyzer>(calc);
}

[Test]
public void Push1Jumpdest_Over10k_Use_JumpdestAnalyzer()
public void Push1Jumpdest_Over10k()
{
byte[] code = new byte[10_001];
for (int i = 0; i < code.Length; i++)
{
code[i] = i % 2 == 0 ? (byte)0x60 : (byte)0x5b;
}

ICodeInfoAnalyzer calc = null;
int iterations = 1;
while (iterations <= 10)
{
CodeInfo codeInfo = new(code);

codeInfo.ValidateJump(10, false).Should().BeFalse();
codeInfo.ValidateJump(11, false).Should().BeFalse(); // 0x5b but not JUMPDEST but data

FieldInfo field = typeof(CodeInfo).GetField(AnalyzerField, BindingFlags.Instance | BindingFlags.NonPublic);
calc = (ICodeInfoAnalyzer)field.GetValue(codeInfo);

if (calc is JumpdestAnalyzer)
{
break;
}

iterations++;
}
CodeInfo codeInfo = new(code);

Assert.IsInstanceOf<JumpdestAnalyzer>(calc);
codeInfo.ValidateJump(10, false).Should().BeFalse();
codeInfo.ValidateJump(11, false).Should().BeFalse(); // 0x5b but not JUMPDEST but data
}
}
}
170 changes: 0 additions & 170 deletions src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeDataAnalyzer.cs

This file was deleted.

46 changes: 9 additions & 37 deletions src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Runtime.CompilerServices;

using Nethermind.Evm.Precompiles;

namespace Nethermind.Evm.CodeAnalysis
{
public class CodeInfo
{
private const int SampledCodeLength = 10_001;
private const int PercentageOfPush1 = 40;
private const int NumberOfSamples = 100;
private static readonly Random _rand = new();

public byte[] MachineCode { get; set; }
public IPrecompile? Precompile { get; set; }
private ICodeInfoAnalyzer? _analyzer;
private JumpDestinationAnalyzer? _analyzer;

public CodeInfo(byte[] code)
{
Expand All @@ -32,45 +29,20 @@ public CodeInfo(IPrecompile precompile)

public bool ValidateJump(int destination, bool isSubroutine)
{
if (_analyzer is null)
{
CreateAnalyzer();
}
JumpDestinationAnalyzer analyzer = _analyzer;
analyzer ??= CreateAnalyzer();

return _analyzer.ValidateJump(destination, isSubroutine);
return analyzer.ValidateJump(destination, isSubroutine);
}

/// <summary>
/// Do sampling to choose an algo when the code is big enough.
/// When the code size is small we can use the default analyzer.
/// </summary>
private void CreateAnalyzer()
[MethodImpl(MethodImplOptions.NoInlining)]
private JumpDestinationAnalyzer CreateAnalyzer()
{
if (MachineCode.Length >= SampledCodeLength)
{
byte push1Count = 0;

// we check (by sampling randomly) how many PUSH1 instructions are in the code
for (int i = 0; i < NumberOfSamples; i++)
{
byte instruction = MachineCode[_rand.Next(0, MachineCode.Length)];

// PUSH1
if (instruction == 0x60)
{
push1Count++;
}
}

// If there are many PUSH1 ops then use the JUMPDEST analyzer.
// The JumpdestAnalyzer can perform up to 40% better than the default Code Data Analyzer
// in a scenario when the code consists only of PUSH1 instructions.
_analyzer = push1Count > PercentageOfPush1 ? new JumpdestAnalyzer(MachineCode) : new CodeDataAnalyzer(MachineCode);
}
else
{
_analyzer = new CodeDataAnalyzer(MachineCode);
}
return _analyzer = new JumpDestinationAnalyzer(MachineCode);
}
}
}
Loading