Skip to content

Commit 016b009

Browse files
authored
Use a profiler to improve linker performance (#291)
After measuring the impact of #286, #287, and #290 and seeing it to be too modest. I decided to use a memory profiler, and it found "the good stuff". These changes had the largest impact on allocations and performance. When linking inputs that come from descriptor protos (as opposed to inputs that are compiled from sources and have ASTs), this resulted in a 23% reduction in latency and 70% reduction in allocations. This change features the following improvements: 1. `ast.NoSourceNode` now has a pointer receiver, so wrapping one in an `ast.Node` interface value doesn't incur an allocation to put the value on the heap. This also updates `parser.ParseResult` to refer to a single `*ast.NoSourceNode` when it has no AST, instead of allocating one in each call to get a node value. The `NoSourceNode`'s underlying type is now `ast.FileInfo` so that it can be allocation-free, even for the `NodeInfo` method (which previously was allocating a new `FileInfo` each time). 3. Don't allocate a slice to hold the set of checked files for each element being resolved. Instead, we allocate a single slice up front, and re-use that throughout. 4. Don't pro-actively allocate strings that only are used for error messages; instead defer construction of the change to the construction of the error.
1 parent 93923d2 commit 016b009

File tree

11 files changed

+115
-107
lines changed

11 files changed

+115
-107
lines changed

ast/enum.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type EnumValueDeclNode interface {
108108
}
109109

110110
var _ EnumValueDeclNode = (*EnumValueNode)(nil)
111-
var _ EnumValueDeclNode = NoSourceNode{}
111+
var _ EnumValueDeclNode = (*NoSourceNode)(nil)
112112

113113
// EnumValueNode represents an enum declaration. Example:
114114
//

ast/field.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var _ FieldDeclNode = (*FieldNode)(nil)
4141
var _ FieldDeclNode = (*GroupNode)(nil)
4242
var _ FieldDeclNode = (*MapFieldNode)(nil)
4343
var _ FieldDeclNode = (*SyntheticMapField)(nil)
44-
var _ FieldDeclNode = NoSourceNode{}
44+
var _ FieldDeclNode = (*NoSourceNode)(nil)
4545

4646
// FieldNode represents a normal field declaration (not groups or maps). It
4747
// can represent extension fields as well as non-extension fields (both inside
@@ -394,7 +394,7 @@ type OneofDeclNode interface {
394394

395395
var _ OneofDeclNode = (*OneofNode)(nil)
396396
var _ OneofDeclNode = (*SyntheticOneof)(nil)
397-
var _ OneofDeclNode = NoSourceNode{}
397+
var _ OneofDeclNode = (*NoSourceNode)(nil)
398398

399399
// OneofNode represents a one-of declaration. Example:
400400
//

ast/file.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type FileDeclNode interface {
2525
}
2626

2727
var _ FileDeclNode = (*FileNode)(nil)
28-
var _ FileDeclNode = NoSourceNode{}
28+
var _ FileDeclNode = (*NoSourceNode)(nil)
2929

3030
// FileNode is the root of the AST hierarchy. It represents an entire
3131
// protobuf source file.

ast/message.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type MessageDeclNode interface {
3232
var _ MessageDeclNode = (*MessageNode)(nil)
3333
var _ MessageDeclNode = (*SyntheticGroupMessageNode)(nil)
3434
var _ MessageDeclNode = (*SyntheticMapEntryNode)(nil)
35-
var _ MessageDeclNode = NoSourceNode{}
35+
var _ MessageDeclNode = (*NoSourceNode)(nil)
3636

3737
// MessageNode represents a message declaration. Example:
3838
//

ast/no_source.go

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,104 +41,102 @@ func (s unknownSpan) End() SourcePos {
4141
// NoSourceNode is a placeholder AST node that implements numerous
4242
// interfaces in this package. It can be used to represent an AST
4343
// element for a file whose source is not available.
44-
type NoSourceNode struct {
45-
filename string
46-
}
44+
type NoSourceNode FileInfo
4745

4846
// NewNoSourceNode creates a new NoSourceNode for the given filename.
49-
func NewNoSourceNode(filename string) NoSourceNode {
50-
return NoSourceNode{filename: filename}
47+
func NewNoSourceNode(filename string) *NoSourceNode {
48+
return &NoSourceNode{name: filename}
5149
}
5250

53-
func (n NoSourceNode) Name() string {
54-
return n.filename
51+
func (n *NoSourceNode) Name() string {
52+
return n.name
5553
}
5654

57-
func (n NoSourceNode) Start() Token {
55+
func (n *NoSourceNode) Start() Token {
5856
return 0
5957
}
6058

61-
func (n NoSourceNode) End() Token {
59+
func (n *NoSourceNode) End() Token {
6260
return 0
6361
}
6462

65-
func (n NoSourceNode) NodeInfo(Node) NodeInfo {
63+
func (n *NoSourceNode) NodeInfo(Node) NodeInfo {
6664
return NodeInfo{
67-
fileInfo: &FileInfo{name: n.filename},
65+
fileInfo: (*FileInfo)(n),
6866
}
6967
}
7068

71-
func (n NoSourceNode) GetSyntax() Node {
69+
func (n *NoSourceNode) GetSyntax() Node {
7270
return n
7371
}
7472

75-
func (n NoSourceNode) GetName() Node {
73+
func (n *NoSourceNode) GetName() Node {
7674
return n
7775
}
7876

79-
func (n NoSourceNode) GetValue() ValueNode {
77+
func (n *NoSourceNode) GetValue() ValueNode {
8078
return n
8179
}
8280

83-
func (n NoSourceNode) FieldLabel() Node {
81+
func (n *NoSourceNode) FieldLabel() Node {
8482
return n
8583
}
8684

87-
func (n NoSourceNode) FieldName() Node {
85+
func (n *NoSourceNode) FieldName() Node {
8886
return n
8987
}
9088

91-
func (n NoSourceNode) FieldType() Node {
89+
func (n *NoSourceNode) FieldType() Node {
9290
return n
9391
}
9492

95-
func (n NoSourceNode) FieldTag() Node {
93+
func (n *NoSourceNode) FieldTag() Node {
9694
return n
9795
}
9896

99-
func (n NoSourceNode) FieldExtendee() Node {
97+
func (n *NoSourceNode) FieldExtendee() Node {
10098
return n
10199
}
102100

103-
func (n NoSourceNode) GetGroupKeyword() Node {
101+
func (n *NoSourceNode) GetGroupKeyword() Node {
104102
return n
105103
}
106104

107-
func (n NoSourceNode) GetOptions() *CompactOptionsNode {
105+
func (n *NoSourceNode) GetOptions() *CompactOptionsNode {
108106
return nil
109107
}
110108

111-
func (n NoSourceNode) RangeStart() Node {
109+
func (n *NoSourceNode) RangeStart() Node {
112110
return n
113111
}
114112

115-
func (n NoSourceNode) RangeEnd() Node {
113+
func (n *NoSourceNode) RangeEnd() Node {
116114
return n
117115
}
118116

119-
func (n NoSourceNode) GetNumber() Node {
117+
func (n *NoSourceNode) GetNumber() Node {
120118
return n
121119
}
122120

123-
func (n NoSourceNode) MessageName() Node {
121+
func (n *NoSourceNode) MessageName() Node {
124122
return n
125123
}
126124

127-
func (n NoSourceNode) OneofName() Node {
125+
func (n *NoSourceNode) OneofName() Node {
128126
return n
129127
}
130128

131-
func (n NoSourceNode) GetInputType() Node {
129+
func (n *NoSourceNode) GetInputType() Node {
132130
return n
133131
}
134132

135-
func (n NoSourceNode) GetOutputType() Node {
133+
func (n *NoSourceNode) GetOutputType() Node {
136134
return n
137135
}
138136

139-
func (n NoSourceNode) Value() interface{} {
137+
func (n *NoSourceNode) Value() interface{} {
140138
return nil
141139
}
142140

143-
func (n NoSourceNode) RangeOptions(func(*OptionNode) bool) {
141+
func (n *NoSourceNode) RangeOptions(func(*OptionNode) bool) {
144142
}

ast/options.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type OptionDeclNode interface {
2626
}
2727

2828
var _ OptionDeclNode = (*OptionNode)(nil)
29-
var _ OptionDeclNode = NoSourceNode{}
29+
var _ OptionDeclNode = (*NoSourceNode)(nil)
3030

3131
// OptionNode represents the declaration of a single option for an element.
3232
// It is used both for normal option declarations (start with "option" keyword
@@ -410,4 +410,4 @@ var _ NodeWithOptions = RPCDeclNode(nil)
410410
var _ NodeWithOptions = FieldDeclNode(nil)
411411
var _ NodeWithOptions = EnumValueDeclNode(nil)
412412
var _ NodeWithOptions = (*ExtensionRangeNode)(nil)
413-
var _ NodeWithOptions = NoSourceNode{}
413+
var _ NodeWithOptions = (*NoSourceNode)(nil)

ast/ranges.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type RangeDeclNode interface {
108108
}
109109

110110
var _ RangeDeclNode = (*RangeNode)(nil)
111-
var _ RangeDeclNode = NoSourceNode{}
111+
var _ RangeDeclNode = (*NoSourceNode)(nil)
112112

113113
// RangeNode represents a range expression, used in both extension ranges and
114114
// reserved ranges. Example:

ast/service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type RPCDeclNode interface {
108108
}
109109

110110
var _ RPCDeclNode = (*RPCNode)(nil)
111-
var _ RPCDeclNode = NoSourceNode{}
111+
var _ RPCDeclNode = (*NoSourceNode)(nil)
112112

113113
// RPCNode represents an RPC declaration. Example:
114114
//

ast/values.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var _ ValueNode = (*SpecialFloatLiteralNode)(nil)
5454
var _ ValueNode = (*SignedFloatLiteralNode)(nil)
5555
var _ ValueNode = (*ArrayLiteralNode)(nil)
5656
var _ ValueNode = (*MessageLiteralNode)(nil)
57-
var _ ValueNode = NoSourceNode{}
57+
var _ ValueNode = (*NoSourceNode)(nil)
5858

5959
// StringValueNode is an AST node that represents a string literal.
6060
// Such a node can be a single literal (*StringLiteralNode) or a

0 commit comments

Comments
 (0)