Skip to content

Commit 9d96b4c

Browse files
committed
Garbage-collect expression ASTs
In parse_and_visit_expression, we know that an allocated expression isn't used after calling visit_expression. Save memory by deleting the entire expression tree allocated by parse_expression after calling visit_expression. This commit should not change behavior. This memory usage optimization improves parsing performance: -------------------------------------------------------------------------- When Benchmark Time CPU Iterations -------------------------------------------------------------------------- before benchmark_parse_file 1634589 ns 1632770 ns 1710 after benchmark_parse_file 1621339 ns 1619349 ns 1747
1 parent b3da0e4 commit 9d96b4c

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

src/quick-lint-js/expression.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ class expression_arena {
130130
return &this->buffering_visitor_memory_;
131131
}
132132

133+
monotonic_allocator *allocator() noexcept { return &this->allocator_; }
134+
133135
private:
134136
template <class T, class... Args>
135137
T *allocate(Args &&... args) {

src/quick-lint-js/linked-bump-allocator.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
#include <sanitizer/asan_interface.h>
1818
#endif
1919

20+
#if defined(QLJS_DEBUG) && QLJS_DEBUG
21+
#define QLJS_DEBUG_BUMP_ALLOCATOR 1
22+
#else
23+
#define QLJS_DEBUG_BUMP_ALLOCATOR 0
24+
#endif
25+
2026
namespace quick_lint_js {
2127
// A memory allocator with a few features:
2228
//
@@ -121,6 +127,7 @@ class linked_bump_allocator : public boost::container::pmr::memory_resource {
121127
template <class T>
122128
bool try_grow_array_in_place(T* array, std::size_t old_size,
123129
std::size_t new_size) {
130+
this->assert_not_disabled();
124131
QLJS_ASSERT(new_size > old_size);
125132
std::size_t old_byte_size = this->align_up(old_size * sizeof(T));
126133
bool array_is_last_allocation =
@@ -156,6 +163,33 @@ class linked_bump_allocator : public boost::container::pmr::memory_resource {
156163
return this;
157164
}
158165

166+
class disable_guard {
167+
public:
168+
~disable_guard() {
169+
#if QLJS_DEBUG_BUMP_ALLOCATOR
170+
this->alloc_->disabled_count_ -= 1;
171+
#endif
172+
}
173+
174+
private:
175+
#if QLJS_DEBUG_BUMP_ALLOCATOR
176+
explicit disable_guard(linked_bump_allocator* alloc) noexcept
177+
: alloc_(alloc) {
178+
this->alloc_->disabled_count_ -= 1;
179+
}
180+
#else
181+
explicit disable_guard(linked_bump_allocator*) noexcept {}
182+
#endif
183+
184+
#if QLJS_DEBUG_BUMP_ALLOCATOR
185+
linked_bump_allocator* alloc_;
186+
#endif
187+
188+
friend class linked_bump_allocator;
189+
};
190+
191+
[[nodiscard]] disable_guard disable() noexcept { return disable_guard(this); }
192+
159193
protected:
160194
void* do_allocate(std::size_t bytes, std::size_t align) override {
161195
QLJS_ASSERT(align <= Alignment);
@@ -230,6 +264,7 @@ class linked_bump_allocator : public boost::container::pmr::memory_resource {
230264
#endif
231265

232266
[[nodiscard]] void* allocate_bytes(std::size_t size) {
267+
this->assert_not_disabled();
233268
QLJS_SLOW_ASSERT(size % Alignment == 0);
234269
if (this->remaining_bytes_in_current_chunk() < size) {
235270
this->append_chunk(maximum(size, this->default_chunk_size));
@@ -268,9 +303,23 @@ class linked_bump_allocator : public boost::container::pmr::memory_resource {
268303
this->chunk_end_ = this->chunk_->data_end();
269304
}
270305

306+
void assert_not_disabled() const {
307+
#if QLJS_DEBUG_BUMP_ALLOCATOR
308+
QLJS_ALWAYS_ASSERT(!this->is_disabled());
309+
#endif
310+
}
311+
312+
#if QLJS_DEBUG_BUMP_ALLOCATOR
313+
bool is_disabled() const noexcept { return this->disabled_count_ > 0; }
314+
#endif
315+
271316
chunk_header* chunk_ = nullptr;
272317
char* next_allocation_ = nullptr;
273318
char* chunk_end_ = nullptr;
319+
320+
#if QLJS_DEBUG_BUMP_ALLOCATOR
321+
int disabled_count_ = 0;
322+
#endif
274323
};
275324
}
276325

src/quick-lint-js/parse.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3673,8 +3673,16 @@ class parser {
36733673

36743674
template <QLJS_PARSE_VISITOR Visitor>
36753675
void parse_and_visit_expression(Visitor &v, precedence prec) {
3676+
monotonic_allocator &alloc = *this->expressions_.allocator();
3677+
auto rewind_state = alloc.prepare_for_rewind();
3678+
36763679
expression *ast = this->parse_expression(prec);
3677-
this->visit_expression(ast, v, variable_context::rhs);
3680+
{
3681+
auto disable_guard = alloc.disable();
3682+
this->visit_expression(ast, v, variable_context::rhs);
3683+
}
3684+
3685+
alloc.rewind(std::move(rewind_state));
36783686
}
36793687

36803688
expression *parse_expression(precedence);

0 commit comments

Comments
 (0)