Skip to content

Commit

Permalink
sharding :Serialization Perf (ethereum#147)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Yutaro Mori authored and prestonvanloon committed Jun 3, 2018
1 parent b5ae220 commit 84d139d
Show file tree
Hide file tree
Showing 4 changed files with 425 additions and 163 deletions.
60 changes: 32 additions & 28 deletions sharding/collation.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,61 +111,65 @@ func (c *Collation) CalculateChunkRoot() {
c.header.data.ChunkRoot = &chunkRoot
}

// ConvertBackToTx converts raw blobs back to their original transactions.
func ConvertBackToTx(rawBlobs []utils.RawBlob) ([]*types.Transaction, error) {

blobs := make([]*types.Transaction, len(rawBlobs))

for i := 0; i < len(rawBlobs); i++ {

blobs[i] = types.NewTransaction(0, common.HexToAddress("0x"), nil, 0, nil, nil)

err := utils.ConvertFromRawBlob(&rawBlobs[i], blobs[i])
// convertTxToRawBlob transactions into RawBlobs. This step encodes transactions uses RLP encoding
func convertTxToRawBlob(txs []*types.Transaction) ([]*utils.RawBlob, error) {
blobs := make([]*utils.RawBlob, len(txs))
for i := 0; i < len(txs); i++ {
err := error(nil)
blobs[i], err = utils.NewRawBlob(txs[i], false)
if err != nil {
return nil, fmt.Errorf("Creation of transactions from raw blobs failed: %v", err)
return nil, err
}
}
return blobs, nil

}

// SerializeTxToBlob method serializes the input tx
// and returns the blobs in byte array.
// SerializeTxToBlob converts transactions using two steps. First performs RLP encoding, and then blob encoding.
func SerializeTxToBlob(txs []*types.Transaction) ([]byte, error) {

blobs := make([]*utils.RawBlob, len(txs))
for i := 0; i < len(txs); i++ {
err := error(nil)
blobs[i], err = utils.NewRawBlob(txs[i], false)
if err != nil {
return nil, fmt.Errorf("%v", err)
}
blobs, err := convertTxToRawBlob(txs)
if err != nil {
return nil, err
}

serializedTx, err := utils.Serialize(blobs)
if err != nil {
return nil, fmt.Errorf("%v", err)
return nil, err
}

if int64(len(serializedTx)) > collationSizelimit {
return nil, fmt.Errorf("The serialized body exceeded the collation size limit: %v", serializedTx)
return nil, fmt.Errorf("the serialized body size %d exceeded the collation size limit %d", len(serializedTx), collationSizelimit)
}

return serializedTx, nil
}

// convertRawBlobToTx converts raw blobs back to their original transactions.
func convertRawBlobToTx(rawBlobs []utils.RawBlob) ([]*types.Transaction, error) {
blobs := make([]*types.Transaction, len(rawBlobs))

for i := 0; i < len(rawBlobs); i++ {
blobs[i] = types.NewTransaction(0, common.HexToAddress("0x"), nil, 0, nil, nil)

err := utils.ConvertFromRawBlob(&rawBlobs[i], blobs[i])
if err != nil {
return nil, fmt.Errorf("creation of transactions from raw blobs failed: %v", err)
}
}
return blobs, nil
}

// DeserializeBlobToTx takes byte array blob and converts it back
// to original txs and returns the txs in tx array.
func DeserializeBlobToTx(serialisedBlob []byte) (*[]*types.Transaction, error) {

deserializedBlobs, err := utils.Deserialize(serialisedBlob)
if err != nil {
return nil, fmt.Errorf("%v", err)
return nil, err
}

txs, err := ConvertBackToTx(deserializedBlobs)
txs, err := convertRawBlobToTx(deserializedBlobs)

if err != nil {
return nil, fmt.Errorf("%v", err)
return nil, err
}

return &txs, nil
Expand Down
151 changes: 139 additions & 12 deletions sharding/collation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"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
Expand Down Expand Up @@ -131,34 +132,160 @@ func makeTxWithGasLimit(gl uint64) *types.Transaction {
// BENCHMARK TESTS

// Helper function to generate test that completes round trip serialization tests for a specific number of transactions.
func runBenchTest(b *testing.B, numTransactions int) {
func makeRandomTransactions(numTransactions int) []*types.Transaction {
var txs []*types.Transaction
for i := 0; i < numTransactions; i++ {
data := make([]byte, 650)
// 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++ {
results, _ := SerializeTxToBlob(txs)
_, _ = DeserializeBlobToTx(results)
_, 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 BenchmarkSerialization10(b *testing.B) {
runBenchTest(b, 10)
func BenchmarkDeserializeNoRLP1000(b *testing.B) {
runDeserializeNoRLPBenchmark(b, 1000)
}

func BenchmarkSerialization100(b *testing.B) {
runBenchTest(b, 100)
func BenchmarkSerializeRoundtrip10(b *testing.B) {
runSerializeRoundtrip(b, 10)
}

func BenchmarkSerialization1000(b *testing.B) {
runBenchTest(b, 1000)
func BenchmarkSerializeRoundtrip100(b *testing.B) {
runSerializeRoundtrip(b, 100)
}

func BenchmarkSerialization10000(b *testing.B) {
runBenchTest(b, 10000)
func BenchmarkSerializeRoundtrip1000(b *testing.B) {
runSerializeRoundtrip(b, 1000)
}
Loading

0 comments on commit 84d139d

Please sign in to comment.