Skip to content

Commit 7ffd9a5

Browse files
authored
Merge pull request #4019 from ruby/opt2
Another round of optimizations
2 parents 7ac0c85 + 56cdcbb commit 7ffd9a5

7 files changed

Lines changed: 91 additions & 33 deletions

File tree

ext/prism/extension.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -802,16 +802,14 @@ parse_lex_input(const uint8_t *input, size_t input_length, const pm_options_t *o
802802

803803
pm_node_t *node = pm_parse(parser);
804804

805-
// Here we need to update the Source object to have the correct
806-
// encoding for the source string and the correct newline offsets.
807-
// We do it here because we've already created the Source object and given
808-
// it over to all of the tokens, and both of these are only set after pm_parse().
805+
/* Update the Source object with the correct encoding and line offsets,
806+
* which are only available after pm_parse() completes. */
809807
rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
810808
rb_enc_associate(source_string, encoding);
811809

812810
const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
813811
for (size_t index = 0; index < line_offsets->size; index++) {
814-
rb_ary_push(offsets, ULONG2NUM(line_offsets->offsets[index]));
812+
rb_ary_store(offsets, (long) index, ULONG2NUM(line_offsets->offsets[index]));
815813
}
816814

817815
if (pm_options_freeze(options)) {

include/prism/internal/parser.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,18 @@ struct pm_parser_t {
582582
*/
583583
uint32_t node_id;
584584

585+
/*
586+
* A single-entry cache for pm_parser_constant_id_raw. Avoids redundant
587+
* constant pool lookups when the same token is resolved multiple times
588+
* (e.g., once during lexing for local variable detection, and again
589+
* during parsing for node creation).
590+
*/
591+
struct {
592+
const uint8_t *start;
593+
const uint8_t *end;
594+
pm_constant_id_t id;
595+
} constant_cache;
596+
585597
/* The current state of the lexer. */
586598
pm_lex_state_t lex_state;
587599

lib/prism/parse_result.rb

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,26 @@ def self.for(source, start_line, offsets)
5858
# The line number where this source starts.
5959
attr_reader :start_line #: Integer
6060

61-
# The list of newline byte offsets in the source code.
62-
attr_reader :offsets #: Array[Integer]
61+
# The list of newline byte offsets in the source code. When initialized from
62+
# the C extension, this may be a packed binary string of uint32_t values
63+
# that is lazily unpacked on first access.
64+
#--
65+
#: () -> Array[Integer]
66+
def offsets
67+
offsets = @offsets
68+
return offsets if offsets.is_a?(Array)
69+
@offsets = offsets.unpack("L*")
70+
end
6371

64-
# Create a new source object with the given source code.
72+
# Create a new source object with the given source code. The offsets
73+
# parameter can be either an Array of Integer byte offsets or a packed
74+
# binary string of uint32_t values (from the C extension).
6575
#--
66-
#: (String source, Integer start_line, Array[Integer] offsets) -> void
76+
#: (String source, Integer start_line, Array[Integer] | String offsets) -> void
6777
def initialize(source, start_line, offsets)
6878
@source = source
69-
@start_line = start_line # set after parsing is done
70-
@offsets = offsets # set after parsing is done
79+
@start_line = start_line
80+
@offsets = offsets
7181
end
7282

7383
# Replace the value of start_line with the given value.
@@ -81,7 +91,7 @@ def replace_start_line(start_line)
8191
#--
8292
#: (Array[Integer] offsets) -> void
8393
def replace_offsets(offsets)
84-
@offsets.replace(offsets)
94+
@offsets = offsets
8595
end
8696

8797
# Returns the encoding of the source code, which is set by parameters to the

rbi/generated/prism/parse_result.rbi

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

sig/generated/prism/parse_result.rbs

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

src/prism.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,19 @@ pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t
11201120
*/
11211121
static PRISM_INLINE pm_constant_id_t
11221122
pm_parser_constant_id_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1123-
return pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start));
1123+
/* Fast path: if this is the same token as the last lookup (same pointer
1124+
* range), return the cached result. */
1125+
if (start == parser->constant_cache.start && end == parser->constant_cache.end) {
1126+
return parser->constant_cache.id;
1127+
}
1128+
1129+
pm_constant_id_t id = pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start));
1130+
1131+
parser->constant_cache.start = start;
1132+
parser->constant_cache.end = end;
1133+
parser->constant_cache.id = id;
1134+
1135+
return id;
11241136
}
11251137

11261138
/**
@@ -12589,6 +12601,14 @@ match4(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2,
1258912601
return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
1259012602
}
1259112603

12604+
/**
12605+
* Returns true if the current token is any of the six given types.
12606+
*/
12607+
static PRISM_INLINE bool
12608+
match6(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6) {
12609+
return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
12610+
}
12611+
1259212612
/**
1259312613
* Returns true if the current token is any of the seven given types.
1259412614
*/
@@ -15091,6 +15111,16 @@ parse_block(pm_parser_t *parser, uint16_t depth) {
1509115111
*/
1509215112
static bool
1509315113
parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_block, uint8_t flags, uint16_t depth) {
15114+
/* Fast path: if the current token can't begin an expression and isn't
15115+
* a parenthesis, block opener, or splat/block-pass operator, there are
15116+
* no arguments to parse. */
15117+
if (
15118+
!token_begins_expression_p(parser->current.type) &&
15119+
!match6(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_KEYWORD_DO_BLOCK, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)
15120+
) {
15121+
return false;
15122+
}
15123+
1509415124
bool found = false;
1509515125
bool parsed_command_args = false;
1509615126

templates/ext/prism/api_node.c.erb

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>"
22
#include "prism/extension.h"
33
#include "prism/internal/allocator.h"
4+
#include "prism/internal/arena.h"
45

56
#include <assert.h>
67

@@ -81,11 +82,7 @@ pm_source_new(const pm_parser_t *parser, rb_encoding *encoding, bool freeze) {
8182
VALUE source_string = rb_enc_str_new((const char *) start, pm_parser_end(parser) - start, encoding);
8283

8384
const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
84-
VALUE offsets = rb_ary_new_capa(line_offsets->size);
85-
86-
for (size_t index = 0; index < line_offsets->size; index++) {
87-
rb_ary_push(offsets, ULONG2NUM(line_offsets->offsets[index]));
88-
}
85+
VALUE offsets = rb_str_new((const char *) line_offsets->offsets, line_offsets->size * sizeof(uint32_t));
8986

9087
if (freeze) {
9188
rb_obj_freeze(source_string);
@@ -105,8 +102,8 @@ typedef struct pm_node_stack_node {
105102
} pm_node_stack_node_t;
106103

107104
static void
108-
pm_node_stack_push(pm_node_stack_node_t **stack, const pm_node_t *visit) {
109-
pm_node_stack_node_t *node = xmalloc(sizeof(pm_node_stack_node_t));
105+
pm_node_stack_push(pm_arena_t *arena, pm_node_stack_node_t **stack, const pm_node_t *visit) {
106+
pm_node_stack_node_t *node = (pm_node_stack_node_t *) pm_arena_alloc(arena, sizeof(pm_node_stack_node_t), PRISM_ALIGNOF(pm_node_stack_node_t));
110107
node->prev = *stack;
111108
node->visit = visit;
112109
node->visited = false;
@@ -119,7 +116,6 @@ pm_node_stack_pop(pm_node_stack_node_t **stack) {
119116
const pm_node_t *visit = current->visit;
120117

121118
*stack = current->prev;
122-
xfree_sized(current, sizeof(pm_node_stack_node_t));
123119

124120
return visit;
125121
}
@@ -151,8 +147,9 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi
151147
pm_ast_constants_each_data_t constants_data = { .constants = constants, .encoding = encoding };
152148
pm_parser_constants_each(parser, pm_ast_constants_each, &constants_data);
153149

150+
pm_arena_t *node_arena = pm_arena_new();
154151
pm_node_stack_node_t *node_stack = NULL;
155-
pm_node_stack_push(&node_stack, node);
152+
pm_node_stack_push(node_arena, &node_stack, node);
156153
VALUE value_stack = rb_ary_new();
157154

158155
while (node_stack != NULL) {
@@ -175,10 +172,10 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi
175172
<%- node.fields.each do |field| -%>
176173
<%- case field -%>
177174
<%- when Prism::Template::NodeField, Prism::Template::OptionalNodeField -%>
178-
pm_node_stack_push(&node_stack, (pm_node_t *) cast-><%= field.name %>);
175+
pm_node_stack_push(node_arena, &node_stack, (pm_node_t *) cast-><%= field.name %>);
179176
<%- when Prism::Template::NodeListField -%>
180177
for (size_t index = 0; index < cast-><%= field.name %>.size; index++) {
181-
pm_node_stack_push(&node_stack, (pm_node_t *) cast-><%= field.name %>.nodes[index]);
178+
pm_node_stack_push(node_arena, &node_stack, (pm_node_t *) cast-><%= field.name %>.nodes[index]);
182179
}
183180
<%- end -%>
184181
<%- end -%>
@@ -280,6 +277,7 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi
280277
}
281278
}
282279

280+
pm_arena_free(node_arena);
283281
return rb_ary_pop(value_stack);
284282
}
285283

0 commit comments

Comments
 (0)