Significantly reduce size of block_t

A `block_t` instance is allocated for each live block type in memory when
executing a script or snippet of fish code. While many of the items in a
`block_t` class are specific to a particular type of block, the overhead of
`maybe_t<event_t>` that's unused except in the relatively extremely rare case of
an event block is more significant than the rest, given that 88 out of the 216
bytes of a `block_t` are set aside for this field that is rarely used.

This patch reorders the `block_t` members by order of decreasing alignment,
bringing down the size to 208 bytes, then changes `maybe_t<event_t>` to
`shared_ptr<event_t>` instead of allocating room for the event on the stack.
This brings down the runtime memory size of a `block_t` to 136 bytes for a 37%
reduction in size.

I would like to investigate using inheritance and virtual methods to have a
`block_t` only include the values that actually make sense for the block rather
than always allocating some sort of storage for them and then only sometimes
using it. In addition to further reducing the memory, I think this could also be
a safer and saner approach overall, as it would make it very clear when and
where we can expect each block_type_type_t-dependent member to be present and
hold a value.
This commit is contained in:
Mahmoud Al-Qudsi 2022-10-24 21:04:17 -05:00
parent 44c9c51841
commit 84b53b4cae
2 changed files with 25 additions and 18 deletions

View file

@ -740,7 +740,7 @@ block_t block_t::if_block() { return block_t(block_type_t::if_block); }
block_t block_t::event_block(event_t evt) { block_t block_t::event_block(event_t evt) {
block_t b{block_type_t::event}; block_t b{block_type_t::event};
b.event = std::move(evt); b.event.reset(new event_t(std::move(evt)));
return b; return b;
} }

View file

@ -38,7 +38,7 @@ inline bool event_block_list_blocks_type(const event_blockage_list_t &ebls) {
} }
/// Types of blocks. /// Types of blocks.
enum class block_type_t { enum class block_type_t : uint16_t {
while_block, /// While loop block while_block, /// While loop block
for_block, /// For loop block for_block, /// For loop block
if_block, /// If block if_block, /// If block
@ -63,40 +63,47 @@ enum class loop_status_t {
/// block_t represents a block of commands. /// block_t represents a block of commands.
class block_t { class block_t {
private:
/// Construct from a block type. /// Construct from a block type.
explicit block_t(block_type_t t); explicit block_t(block_type_t t);
/// Type of block.
const block_type_t block_type;
public: public:
/// Name of file that created this block. // If this is a function block, the function name. Otherwise empty.
filename_ref_t src_filename{}; wcstring function_name{};
/// Line number where this block was created.
int src_lineno{0};
/// Whether we should pop the environment variable stack when we're popped off of the block
/// stack.
bool wants_pop_env{false};
/// List of event blocks. /// List of event blocks.
event_blockage_list_t event_blocks{}; event_blockage_list_t event_blocks{};
// If this is a function block, the function name and arguments. // If this is a function block, the function args. Otherwise empty.
// Otherwise empty.
wcstring function_name{};
wcstring_list_t function_args{}; wcstring_list_t function_args{};
/// Name of file that created this block.
filename_ref_t src_filename{};
// If this is an event block, the event. Otherwise ignored.
std::shared_ptr<event_t> event;
// If this is a source block, the source'd file, interned. // If this is a source block, the source'd file, interned.
// Otherwise nothing. // Otherwise nothing.
filename_ref_t sourced_file{}; filename_ref_t sourced_file{};
// If this is an event block, the event. Otherwise ignored. /// Line number where this block was created.
maybe_t<event_t> event; int src_lineno{0};
block_type_t type() const { return this->block_type; } private:
/// Type of block.
const block_type_t block_type;
public:
/// Whether we should pop the environment variable stack when we're popped off of the block
/// stack.
bool wants_pop_env{false};
/// Description of the block, for debugging. /// Description of the block, for debugging.
wcstring description() const; wcstring description() const;
block_type_t type() const { return this->block_type; }
/// \return if we are a function call (with or without shadowing). /// \return if we are a function call (with or without shadowing).
bool is_function_call() const { bool is_function_call() const {
return type() == block_type_t::function_call || return type() == block_type_t::function_call ||