Skip to content

Commit

Permalink
Optimize Jump Destination Analysis (#6502)
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams authored Jan 12, 2024
1 parent df6e56d commit 8e4ff07
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 377 deletions.

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

0 comments on commit 8e4ff07

Please sign in to comment.