Add benchmark for BlockHashOperation (#203)

Includes a reusable OperationBenchmarkHelper to make writing EVM opcode benchmarks easier in future.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Adrian Sutton 6 years ago committed by GitHub
parent 8fa45725f2
commit ab77bce927
  1. 2
      ethereum/blockcreation/src/main/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashBlockCreator.java
  2. 5
      ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashBlockCreatorTest.java
  3. 2
      ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/EthHashMiningCoordinatorTest.java
  4. 9
      ethereum/core/build.gradle
  5. 72
      ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/BlockHashOperationBenchmark.java
  6. 113
      ethereum/core/src/jmh/java/tech/pegasys/pantheon/ethereum/vm/operations/OperationBenchmarkHelper.java
  7. 72
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java
  8. 8
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/MessageFrameTestFixture.java
  9. 2
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/TestCodeExecutor.java

@ -72,7 +72,7 @@ public class EthHashBlockCreator extends AbstractBlockCreator<Void> {
} catch (final InterruptedException ex) {
throw new CancellationException();
} catch (final ExecutionException ex) {
throw new RuntimeException("Failure occurred during nonce calculations.");
throw new RuntimeException("Failure occurred during nonce calculations.", ex);
}
return BlockHeaderBuilder.create()
.populateFrom(sealableBlockHeader)

@ -19,6 +19,7 @@ import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.EthHashSolver;
import tech.pegasys.pantheon.ethereum.mainnet.EthHasher.Light;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationTestUtils;
import tech.pegasys.pantheon.util.bytes.BytesValue;
@ -41,7 +42,9 @@ public class EthHashBlockCreatorTest {
BytesValue.fromHexString("0x476574682f76312e302e302f6c696e75782f676f312e342e32");
private final ExecutionContextTestFixture executionContextTestFixture =
new ExecutionContextTestFixture();
ExecutionContextTestFixture.builder()
.protocolSchedule(MainnetProtocolSchedule.create(2, 3, 10, 11, 12, -1, 42))
.build();
@Test
public void createMainnetBlock1() throws IOException {

@ -30,7 +30,7 @@ import org.junit.Test;
public class EthHashMiningCoordinatorTest {
private final ExecutionContextTestFixture executionContext = new ExecutionContextTestFixture();
private final ExecutionContextTestFixture executionContext = ExecutionContextTestFixture.create();
private final SyncState syncState = mock(SyncState.class);
private final EthHashMinerExecutor executor = mock(EthHashMinerExecutor.class);
private final EthHashBlockMiner miner = mock(EthHashBlockMiner.class);

@ -50,6 +50,15 @@ dependencies {
testSupportImplementation 'org.assertj:assertj-core'
testSupportImplementation 'org.mockito:mockito-core'
testSupportImplementation 'junit:junit'
jmhImplementation project(':util')
jmhImplementation project( path: ':ethereum:core', configuration: 'testSupportArtifacts')
jmhImplementation project(':crypto')
jmhImplementation project(':ethereum:rlp')
jmhImplementation project(':ethereum:trie')
jmhImplementation project(':services:kvstore')
jmhImplementation 'com.google.guava:guava'
jmhImplementation 'org.openjdk.jmh:jmh-generator-annprocess'
}
configurations { testArtifacts }

@ -0,0 +1,72 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.vm.operations;
import tech.pegasys.pantheon.ethereum.mainnet.ConstantinopleGasCalculator;
import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.util.bytes.Bytes32;
import tech.pegasys.pantheon.util.uint.UInt256;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
@State(Scope.Thread)
public class BlockHashOperationBenchmark {
@Param({
"1", // Worst-case scenario
"125", // Must iterate up the chain
"255" // Hash available directly via current header's parentHash
})
public long blockNumber;
private OperationBenchmarkHelper operationBenchmarkHelper;
private BlockHashOperation operation;
private MessageFrame frame;
@Setup
public void prepare() throws Exception {
operationBenchmarkHelper = OperationBenchmarkHelper.create();
operation = new BlockHashOperation(new ConstantinopleGasCalculator());
frame = operationBenchmarkHelper.createMessageFrame();
}
@TearDown
public void cleanUp() throws Exception {
operationBenchmarkHelper.cleanUp();
}
@Benchmark
public Bytes32 executeOperation() {
frame.pushStackItem(UInt256.of(blockNumber).getBytes());
operation.execute(frame);
return frame.popStackItem();
}
@Benchmark
public Bytes32 executeOperationWithEmptyHashCache() {
final MessageFrame cleanFrame =
operationBenchmarkHelper
.createMessageFrameBuilder()
.blockHashLookup(new BlockHashLookup(frame.getBlockHeader(), frame.getBlockchain()))
.build();
cleanFrame.pushStackItem(UInt256.of(blockNumber).getBytes());
operation.execute(cleanFrame);
return cleanFrame.popStackItem();
}
}

@ -0,0 +1,113 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.vm.operations;
import static java.util.Collections.emptyList;
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockBody;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture;
import tech.pegasys.pantheon.ethereum.core.ExecutionContextTestFixture;
import tech.pegasys.pantheon.ethereum.core.MessageFrameTestFixture;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.services.kvstore.RocksDbKeyValueStorage;
import tech.pegasys.pantheon.util.uint.UInt256;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;
public class OperationBenchmarkHelper {
private final Path storageDirectory;
private final RocksDbKeyValueStorage keyValueStorage;
private final MessageFrame messageFrame;
private OperationBenchmarkHelper(
final Path storageDirectory,
final RocksDbKeyValueStorage keyValueStorage,
final MessageFrame messageFrame) {
this.storageDirectory = storageDirectory;
this.keyValueStorage = keyValueStorage;
this.messageFrame = messageFrame;
}
public static OperationBenchmarkHelper create() throws IOException {
final Path storageDirectory = Files.createTempDirectory("benchmark");
final RocksDbKeyValueStorage keyValueStorage = RocksDbKeyValueStorage.create(storageDirectory);
final ExecutionContextTestFixture executionContext =
ExecutionContextTestFixture.builder().keyValueStorage(keyValueStorage).build();
final MutableBlockchain blockchain = executionContext.getBlockchain();
for (int i = 1; i < 256; i++) {
blockchain.appendBlock(
new Block(
new BlockHeaderTestFixture()
.parentHash(blockchain.getChainHeadHash())
.number(i)
.difficulty(UInt256.ONE)
.buildHeader(),
new BlockBody(emptyList(), emptyList())),
emptyList());
}
final MessageFrame messageFrame =
new MessageFrameTestFixture()
.executionContextTestFixture(executionContext)
.blockHeader(
new BlockHeaderTestFixture()
.parentHash(blockchain.getChainHeadHash())
.number(blockchain.getChainHeadBlockNumber() + 1)
.difficulty(UInt256.ONE)
.buildHeader())
.build();
return new OperationBenchmarkHelper(storageDirectory, keyValueStorage, messageFrame);
}
public MessageFrame createMessageFrame() {
return createMessageFrameBuilder().build();
}
public MessageFrame.Builder createMessageFrameBuilder() {
return MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.messageFrameStack(messageFrame.getMessageFrameStack())
.blockchain(messageFrame.getBlockchain())
.worldState(messageFrame.getWorldState())
.initialGas(messageFrame.getRemainingGas())
.address(messageFrame.getContractAddress())
.originator(messageFrame.getOriginatorAddress())
.contract(messageFrame.getRecipientAddress())
.gasPrice(messageFrame.getGasPrice())
.inputData(messageFrame.getInputData())
.sender(messageFrame.getSenderAddress())
.value(messageFrame.getValue())
.apparentValue(messageFrame.getApparentValue())
.code(messageFrame.getCode())
.blockHeader(messageFrame.getBlockHeader())
.depth(messageFrame.getMessageStackDepth())
.isStatic(messageFrame.isStatic())
.completer(messageFrame -> {})
.miningBeneficiary(messageFrame.getMiningBeneficiary())
.blockHashLookup(messageFrame.getBlockHashLookup());
}
public void cleanUp() throws IOException {
keyValueStorage.close();
MoreFiles.deleteRecursively(storageDirectory, RecursiveDeleteOption.ALLOW_INSECURE);
}
}

@ -27,25 +27,37 @@ import tech.pegasys.pantheon.services.kvstore.KeyValueStorage;
public class ExecutionContextTestFixture {
private final Block genesis = GenesisConfig.mainnet().getBlock();
private final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage();
private final MutableBlockchain blockchain =
new DefaultMutableBlockchain(genesis, keyValueStorage, MainnetBlockHashFunction::createHash);
private final WorldStateArchive stateArchive =
new WorldStateArchive(new KeyValueStorageWorldStateStorage(keyValueStorage));
ProtocolSchedule<Void> protocolSchedule;
ProtocolContext<Void> protocolContext = new ProtocolContext<>(blockchain, stateArchive, null);
public ExecutionContextTestFixture() {
this(MainnetProtocolSchedule.create(2, 3, 10, 11, 12, -1, 42));
}
private final Block genesis;
private final KeyValueStorage keyValueStorage;
private final MutableBlockchain blockchain;
private final WorldStateArchive stateArchive;
private final ProtocolSchedule<Void> protocolSchedule;
private final ProtocolContext<Void> protocolContext;
public ExecutionContextTestFixture(final ProtocolSchedule<Void> protocolSchedule) {
GenesisConfig.mainnet()
.writeStateTo(
new DefaultMutableWorldState(new KeyValueStorageWorldStateStorage(keyValueStorage)));
private ExecutionContextTestFixture(
final ProtocolSchedule<Void> protocolSchedule, final KeyValueStorage keyValueStorage) {
final GenesisConfig<Void> genesisConfig = GenesisConfig.mainnet();
this.genesis = genesisConfig.getBlock();
this.keyValueStorage = keyValueStorage;
this.blockchain =
new DefaultMutableBlockchain(
genesis, keyValueStorage, MainnetBlockHashFunction::createHash);
this.stateArchive =
new WorldStateArchive(new KeyValueStorageWorldStateStorage(keyValueStorage));
this.protocolSchedule = protocolSchedule;
this.protocolContext = new ProtocolContext<>(blockchain, stateArchive, null);
genesisConfig.writeStateTo(
new DefaultMutableWorldState(new KeyValueStorageWorldStateStorage(keyValueStorage)));
}
public static ExecutionContextTestFixture create() {
return new Builder().build();
}
public static Builder builder() {
return new Builder();
}
public Block getGenesis() {
@ -71,4 +83,30 @@ public class ExecutionContextTestFixture {
public ProtocolContext<Void> getProtocolContext() {
return protocolContext;
}
public static class Builder {
private KeyValueStorage keyValueStorage;
private ProtocolSchedule<Void> protocolSchedule;
public Builder keyValueStorage(final KeyValueStorage keyValueStorage) {
this.keyValueStorage = keyValueStorage;
return this;
}
public Builder protocolSchedule(final ProtocolSchedule<Void> protocolSchedule) {
this.protocolSchedule = protocolSchedule;
return this;
}
public ExecutionContextTestFixture build() {
if (protocolSchedule == null) {
protocolSchedule = MainnetProtocolSchedule.create(0, 0, 0, 0, 0, 0, 42);
}
if (keyValueStorage == null) {
keyValueStorage = new InMemoryKeyValueStorage();
}
return new ExecutionContextTestFixture(protocolSchedule, keyValueStorage);
}
}
}

@ -59,6 +59,12 @@ public class MessageFrameTestFixture {
return this;
}
public MessageFrameTestFixture executionContextTestFixture(
final ExecutionContextTestFixture executionContextTestFixture) {
this.executionContextTestFixture = executionContextTestFixture;
return this;
}
public MessageFrameTestFixture blockchain(final Blockchain blockchain) {
this.blockchain = Optional.of(blockchain);
return this;
@ -181,7 +187,7 @@ public class MessageFrameTestFixture {
private ExecutionContextTestFixture getOrCreateExecutionContextTestFixture() {
// Avoid creating a test fixture if the test supplies the blockchain and worldstate.
if (executionContextTestFixture == null) {
executionContextTestFixture = new ExecutionContextTestFixture();
executionContextTestFixture = ExecutionContextTestFixture.create();
}
return executionContextTestFixture;
}

@ -35,7 +35,7 @@ public class TestCodeExecutor {
private static final Address SENDER_ADDRESS = AddressHelpers.ofValue(244259721);
public TestCodeExecutor(final ProtocolSchedule<Void> protocolSchedule) {
fixture = new ExecutionContextTestFixture(protocolSchedule);
fixture = ExecutionContextTestFixture.builder().protocolSchedule(protocolSchedule).build();
}
public MessageFrame executeCode(

Loading…
Cancel
Save