Skip to content

Commit c6cb9dd

Browse files
fjlQi Zhou
authored andcommitted
core/types: faster RLP encoding of Header, StateAcccount, ReceiptForStorage (ethereum#24420)
This change makes use of the new code generator rlp/rlpgen to improve the performance of RLP encoding for Header and StateAccount. It also speeds up encoding of ReceiptForStorage using the new rlp.EncoderBuffer API. The change is much less transparent than I wanted it to be, because Header and StateAccount now have an EncodeRLP method defined with pointer receiver. It used to be possible to encode non-pointer values of these types, but the new method prevents that and attempting to encode unadressable values (even if part of another value) will return an error. The error can be surprising and may pop up in places that previously didn't expect any errors. To make things work, I also needed to update all code paths (mostly in unit tests) that lead to encoding of non-pointer values, and pass a pointer instead. Benchmark results: name old time/op new time/op delta EncodeRLP/legacy-header-8 328ns ± 0% 237ns ± 1% -27.63% (p=0.000 n=8+8) EncodeRLP/london-header-8 353ns ± 0% 247ns ± 1% -30.06% (p=0.000 n=8+8) EncodeRLP/receipt-for-storage-8 237ns ± 0% 123ns ± 0% -47.86% (p=0.000 n=8+7) EncodeRLP/receipt-full-8 297ns ± 0% 301ns ± 1% +1.39% (p=0.000 n=8+8) name old speed new speed delta EncodeRLP/legacy-header-8 1.66GB/s ± 0% 2.29GB/s ± 1% +38.19% (p=0.000 n=8+8) EncodeRLP/london-header-8 1.55GB/s ± 0% 2.22GB/s ± 1% +42.99% (p=0.000 n=8+8) EncodeRLP/receipt-for-storage-8 38.0MB/s ± 0% 64.8MB/s ± 0% +70.48% (p=0.000 n=8+7) EncodeRLP/receipt-full-8 910MB/s ± 0% 897MB/s ± 1% -1.37% (p=0.000 n=8+8) name old alloc/op new alloc/op delta EncodeRLP/legacy-header-8 0.00B 0.00B ~ (all equal) EncodeRLP/london-header-8 0.00B 0.00B ~ (all equal) EncodeRLP/receipt-for-storage-8 64.0B ± 0% 0.0B -100.00% (p=0.000 n=8+8) EncodeRLP/receipt-full-8 320B ± 0% 320B ± 0% ~ (all equal)
1 parent a43f4ec commit c6cb9dd

File tree

16 files changed

+163
-55
lines changed

16 files changed

+163
-55
lines changed

core/types/access_list_tx.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"github.com/ethereum/go-ethereum/common"
2323
)
2424

25-
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
25+
//go:generate go run github.com/fjl/gencodec@latest -type AccessTuple -out gen_access_tuple.go
2626

2727
// AccessList is an EIP-2930 access list.
2828
type AccessList []AccessTuple

core/types/block.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
6363
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
6464
}
6565

66-
//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
66+
//go:generate go run github.com/fjl/gencodec@latest -type Header -field-override headerMarshaling -out gen_header_json.go
67+
//go:generate go run ../../rlp/rlpgen -type Header -out gen_header_rlp.go
6768

6869
// Header represents a block header in the Ethereum blockchain.
6970
type Header struct {

core/types/block_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ func makeBenchBlock() *Block {
285285
func TestRlpDecodeParentHash(t *testing.T) {
286286
// A minimum one
287287
want := common.HexToHash("0x112233445566778899001122334455667788990011223344556677889900aabb")
288-
if rlpData, err := rlp.EncodeToBytes(Header{ParentHash: want}); err != nil {
288+
if rlpData, err := rlp.EncodeToBytes(&Header{ParentHash: want}); err != nil {
289289
t.Fatal(err)
290290
} else {
291291
if have := HeaderParentHashFromRLP(rlpData); have != want {
@@ -299,7 +299,7 @@ func TestRlpDecodeParentHash(t *testing.T) {
299299
// | BaseFee | dynamic| *big.Int | 64 bits |
300300
mainnetTd := new(big.Int)
301301
mainnetTd.SetString("5ad3c2c71bbff854908", 16)
302-
if rlpData, err := rlp.EncodeToBytes(Header{
302+
if rlpData, err := rlp.EncodeToBytes(&Header{
303303
ParentHash: want,
304304
Difficulty: mainnetTd,
305305
Number: new(big.Int).SetUint64(math.MaxUint64),
@@ -316,7 +316,7 @@ func TestRlpDecodeParentHash(t *testing.T) {
316316
{
317317
// The rlp-encoding of the heder belowCauses _total_ length of 65540,
318318
// which is the first to blow the fast-path.
319-
h := Header{
319+
h := &Header{
320320
ParentHash: want,
321321
Extra: make([]byte, 65041),
322322
}

core/types/gen_account_rlp.go

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/gen_header_rlp.go

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/gen_log_rlp.go

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/log.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"github.com/ethereum/go-ethereum/rlp"
2525
)
2626

27-
//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
27+
//go:generate go run github.com/fjl/gencodec@latest -type Log -field-override logMarshaling -out gen_log_json.go
2828

2929
// Log represents a contract log event. These events are generated by the LOG opcode and
3030
// stored/indexed by the node.
@@ -62,15 +62,14 @@ type logMarshaling struct {
6262
Index hexutil.Uint
6363
}
6464

65+
//go:generate go run ../../rlp/rlpgen -type rlpLog -out gen_log_rlp.go
66+
6567
type rlpLog struct {
6668
Address common.Address
6769
Topics []common.Hash
6870
Data []byte
6971
}
7072

71-
// rlpStorageLog is the storage encoding of a log.
72-
type rlpStorageLog rlpLog
73-
7473
// legacyRlpStorageLog is the previous storage encoding of a log including some redundant fields.
7574
type legacyRlpStorageLog struct {
7675
Address common.Address
@@ -85,7 +84,8 @@ type legacyRlpStorageLog struct {
8584

8685
// EncodeRLP implements rlp.Encoder.
8786
func (l *Log) EncodeRLP(w io.Writer) error {
88-
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
87+
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
88+
return rlp.Encode(w, &rl)
8989
}
9090

9191
// DecodeRLP implements rlp.Decoder.
@@ -104,11 +104,8 @@ type LogForStorage Log
104104

105105
// EncodeRLP implements rlp.Encoder.
106106
func (l *LogForStorage) EncodeRLP(w io.Writer) error {
107-
return rlp.Encode(w, rlpStorageLog{
108-
Address: l.Address,
109-
Topics: l.Topics,
110-
Data: l.Data,
111-
})
107+
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
108+
return rlp.Encode(w, &rl)
112109
}
113110

114111
// DecodeRLP implements rlp.Decoder.
@@ -119,7 +116,7 @@ func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
119116
if err != nil {
120117
return err
121118
}
122-
var dec rlpStorageLog
119+
var dec rlpLog
123120
err = rlp.DecodeBytes(blob, &dec)
124121
if err == nil {
125122
*l = LogForStorage{

core/types/receipt.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
"github.com/ethereum/go-ethereum/rlp"
3232
)
3333

34-
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
34+
//go:generate go run github.com/fjl/gencodec@latest -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
3535

3636
var (
3737
receiptStatusFailedRLP = []byte{}
@@ -287,16 +287,20 @@ type ReceiptForStorage Receipt
287287

288288
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
289289
// into an RLP stream.
290-
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
291-
enc := &storedReceiptRLP{
292-
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
293-
CumulativeGasUsed: r.CumulativeGasUsed,
294-
Logs: make([]*LogForStorage, len(r.Logs)),
295-
}
296-
for i, log := range r.Logs {
297-
enc.Logs[i] = (*LogForStorage)(log)
290+
func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
291+
w := rlp.NewEncoderBuffer(_w)
292+
outerList := w.List()
293+
w.WriteBytes((*Receipt)(r).statusEncoding())
294+
w.WriteUint64(r.CumulativeGasUsed)
295+
logList := w.List()
296+
for _, log := range r.Logs {
297+
if err := rlp.Encode(w, log); err != nil {
298+
return err
299+
}
298300
}
299-
return rlp.Encode(w, enc)
301+
w.ListEnd(logList)
302+
w.ListEnd(outerList)
303+
return w.Flush()
300304
}
301305

302306
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation

core/types/state_account.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"github.com/ethereum/go-ethereum/common"
2323
)
2424

25+
//go:generate go run ../../rlp/rlpgen -type StateAccount -out gen_account_rlp.go
26+
2527
// StateAccount is the Ethereum consensus representation of accounts.
2628
// These objects are stored in the main account trie.
2729
type StateAccount struct {

eth/handler_eth_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
570570
t.Fatalf("failed to answer challenge: %v", err)
571571
}
572572
} else {
573-
responseRlp, _ := rlp.EncodeToBytes(types.Header{Number: response.Number})
573+
responseRlp, _ := rlp.EncodeToBytes(&types.Header{Number: response.Number})
574574
if err := remote.ReplyBlockHeadersRLP(request.RequestId, []rlp.RawValue{responseRlp}); err != nil {
575575
t.Fatalf("failed to answer challenge: %v", err)
576576
}

0 commit comments

Comments
 (0)