prysm-pulse/sharding/collation_test.go
Yutaro Mori 01dc3e8b9b sharding :Serialization Perf (#147)
* Serialization performance improvements

* Rename BlobOnly benchmark tests to NoRLP

* Refactor Serialize method

* Add additional tests to serialize

* Formatting

* address PR comments

* doc comments

* linter

* More pr comments


Former-commit-id: 9b01e1cd76eab2dcc9570683c42f578fbfbb2553 [formerly 722f61729adae8236f3cfefde3fdbd0ec66222bb]
Former-commit-id: 26093b4f9d4473a7f7740f6bc66976e0218a7722
2018-06-02 21:14:17 -04:00

292 lines
7.8 KiB
Go

package sharding
import (
"bytes"
"crypto/rand"
"math/big"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/sharding/utils"
)
// fieldAccess is to access unexported fields in structs in another package
func fieldAccess(i interface{}, fields []string) reflect.Value {
val := reflect.ValueOf(i)
for i := 0; i < len(fields); i++ {
val = reflect.Indirect(val).FieldByName(fields[i])
}
return val
}
func TestCollation_Transactions(t *testing.T) {
header := NewCollationHeader(big.NewInt(1), nil, big.NewInt(1), nil, []byte{})
body := []byte{}
transactions := []*types.Transaction{
makeTxWithGasLimit(0),
makeTxWithGasLimit(1),
makeTxWithGasLimit(2),
makeTxWithGasLimit(3),
}
collation := NewCollation(header, body, transactions)
for i, tx := range collation.Transactions() {
if tx.Hash().String() != transactions[i].Hash().String() {
t.Errorf("initialized collation struct does not contain correct transactions")
}
}
}
//TODO: Add test for converting *types.Transaction into raw blobs
//Tests that Transactions can be serialised
func TestSerialize_Deserialize(t *testing.T) {
header := NewCollationHeader(big.NewInt(1), nil, big.NewInt(1), nil, []byte{})
body := []byte{}
transactions := []*types.Transaction{
makeTxWithGasLimit(0),
makeTxWithGasLimit(5),
makeTxWithGasLimit(20),
makeTxWithGasLimit(100),
}
c := NewCollation(header, body, transactions)
tx := c.transactions
results, err := SerializeTxToBlob(tx)
if err != nil {
t.Errorf("Unable to Serialize transactions, %v", err)
}
deserializedTxs, err := DeserializeBlobToTx(results)
if err != nil {
t.Errorf("Unable to deserialize collation body, %v", err)
}
if len(tx) != len(*deserializedTxs) {
t.Errorf("Transaction length is different before and after serialization: %v, %v", len(tx), len(*deserializedTxs))
}
for i := 0; i < len(tx); i++ {
beforeSerialization := tx[i]
afterDeserialization := (*deserializedTxs)[i]
if beforeSerialization.Nonce() != afterDeserialization.Nonce() {
t.Errorf("Data before serialization and after deserialization are not the same ,AccountNonce: %v, %v", beforeSerialization.Nonce(), afterDeserialization.Nonce())
}
if beforeSerialization.Gas() != afterDeserialization.Gas() {
t.Errorf("Data before serialization and after deserialization are not the same ,GasLimit: %v, %v", beforeSerialization.Gas(), afterDeserialization.Gas())
}
if beforeSerialization.GasPrice().Cmp(afterDeserialization.GasPrice()) != 0 {
t.Errorf("Data before serialization and after deserialization are not the same ,Price: %v, %v", beforeSerialization.GasPrice(), afterDeserialization.GasPrice())
}
beforeAddress := reflect.ValueOf(beforeSerialization.To())
afterAddress := reflect.ValueOf(afterDeserialization.To())
if reflect.DeepEqual(beforeAddress, afterAddress) {
t.Errorf("Data before serialization and after deserialization are not the same ,Recipient: %v, %v", beforeAddress, afterAddress)
}
if beforeSerialization.Value().Cmp(afterDeserialization.Value()) != 0 {
t.Errorf("Data before serialization and after deserialization are not the same ,Amount: %v, %v", beforeSerialization.Value(), afterDeserialization.Value())
}
beforeData := beforeSerialization.Data()
afterData := afterDeserialization.Data()
if !bytes.Equal(beforeData, afterData) {
t.Errorf("Data before serialization and after deserialization are not the same ,Payload: %v, %v", beforeData, afterData)
}
}
}
func makeTxWithGasLimit(gl uint64) *types.Transaction {
return types.NewTransaction(0 /*nonce*/, common.HexToAddress("0x0") /*to*/, nil /*amount*/, gl, nil /*gasPrice*/, nil /*data*/)
}
// BENCHMARK TESTS
// Helper function to generate test that completes round trip serialization tests for a specific number of transactions.
func makeRandomTransactions(numTransactions int) []*types.Transaction {
var txs []*types.Transaction
for i := 0; i < numTransactions; i++ {
// 150 is the current average tx size, based on recent blocks (i.e. tx size = block size / # txs)
// for example: https://etherscan.io/block/5722271
data := make([]byte, 150)
rand.Read(data)
txs = append(txs, types.NewTransaction(0 /*nonce*/, common.HexToAddress("0x0") /*to*/, nil /*amount*/, 0 /*gasLimit*/, nil /*gasPrice*/, data))
}
return txs
}
// Benchmarks serialization and deserialization of a set of transactions
func runSerializeRoundtrip(b *testing.B, numTransactions int) {
txs := makeRandomTransactions(numTransactions)
b.ResetTimer()
for i := 0; i < b.N; i++ {
blob, err := SerializeTxToBlob(txs)
if err != nil {
b.Errorf("SerializeTxToBlob failed: %v", err)
}
_, err = DeserializeBlobToTx(blob)
if err != nil {
b.Errorf("DeserializeBlobToTx failed: %v", err)
}
}
}
// Benchmarks serialization of a set of transactions. Does both RLP encoding and serialization of blob
func runSerializeBenchmark(b *testing.B, numTransactions int) {
txs := makeRandomTransactions(numTransactions)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := SerializeTxToBlob(txs)
if err != nil {
b.Errorf("SerializeTxToBlob failed: %v", err)
}
}
}
// Benchmarks just the process of converting an RLP encoded set of transactions into serialized data
func runSerializeNoRLPBenchmark(b *testing.B, numTransactions int) {
txs := makeRandomTransactions(numTransactions)
blobs, err := convertTxToRawBlob(txs)
if err != nil {
b.Errorf("SerializeTxToRawBlock failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := utils.Serialize(blobs)
if err != nil {
b.Errorf("utils.Serialize failed: %v", err)
}
}
}
// Benchmarks deserialization of a set of transactions. Does both deserialization of blob and RLP decoding.
func runDeserializeBenchmark(b *testing.B, numTransactions int) {
txs := makeRandomTransactions(numTransactions)
blob, err := SerializeTxToBlob(txs)
if err != nil {
b.Errorf("SerializeTxToRawBlock failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := DeserializeBlobToTx(blob)
if err != nil {
b.Errorf("DeserializeBlobToTx failed: %v", err)
}
}
}
// Benchmarks just the process of converting serialized data into a blob that's ready for RLP decoding
func runDeserializeNoRLPBenchmark(b *testing.B, numTransactions int) {
txs := makeRandomTransactions(numTransactions)
blob, err := SerializeTxToBlob(txs)
if err != nil {
b.Errorf("SerializeTxToBlob failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := utils.Deserialize(blob)
if err != nil {
b.Errorf("utils.Deserialize failed: %v", err)
}
}
}
func BenchmarkSerializeNoRLP10(b *testing.B) {
runSerializeNoRLPBenchmark(b, 10)
}
func BenchmarkSerializeNoRLP100(b *testing.B) {
runSerializeNoRLPBenchmark(b, 100)
}
func BenchmarkSerializeNoRLP1000(b *testing.B) {
runSerializeNoRLPBenchmark(b, 1000)
}
func BenchmarkSerialize10(b *testing.B) {
runSerializeBenchmark(b, 10)
}
func BenchmarkSerialize100(b *testing.B) {
runSerializeBenchmark(b, 100)
}
func BenchmarkSerialize1000(b *testing.B) {
runSerializeBenchmark(b, 1000)
}
func BenchmarkDeserialize10(b *testing.B) {
runDeserializeBenchmark(b, 10)
}
func BenchmarkDeserialize100(b *testing.B) {
runDeserializeBenchmark(b, 100)
}
func BenchmarkDeserialize1000(b *testing.B) {
runDeserializeBenchmark(b, 1000)
}
func BenchmarkDeserializeNoRLP10(b *testing.B) {
runDeserializeNoRLPBenchmark(b, 10)
}
func BenchmarkDeserializeNoRLP100(b *testing.B) {
runDeserializeNoRLPBenchmark(b, 100)
}
func BenchmarkDeserializeNoRLP1000(b *testing.B) {
runDeserializeNoRLPBenchmark(b, 1000)
}
func BenchmarkSerializeRoundtrip10(b *testing.B) {
runSerializeRoundtrip(b, 10)
}
func BenchmarkSerializeRoundtrip100(b *testing.B) {
runSerializeRoundtrip(b, 100)
}
func BenchmarkSerializeRoundtrip1000(b *testing.B) {
runSerializeRoundtrip(b, 1000)
}