Use delta zigzag encoding for iseq node_id

Node ids are assigned sequentially by the parser and compilation emits
instructions in an order correlated with the tree, so the delta between
consecutive instructions are likely to be small. We can store the delta
between consecutive instructions using zigzag encoding.

On Shopify's monolith, this reduces the boot heap memsize by 20MB.
This commit is contained in:
Peter Zhu committed 2026-10-26 20:21:04 +09:00
1 parent b861e2ba10
commit 3e63685771
2 files changed
+22 -17
Notes: git 2026-10-26 11:21:36 +00:00

No files matched your search

+12 -9
View File
@@ -2626,14 +2626,16 @@ fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
#ifdef USE_ISEQ_NODE_ID
static void
add_node_id(uint8_t *node_ids, unsigned int *node_ids_size, int node_id)
add_node_id(uint8_t *node_ids, unsigned int *node_ids_size, int node_id, int *prev_node_id)
{
uint32_t v = (uint32_t)node_id + 1;
int delta = node_id - *prev_node_id;
uint32_t zz = ((uint32_t)delta << 1) ^ (uint32_t)(delta >> 31);
*prev_node_id = node_id;
do {
uint8_t byte = v & 0x7f;
v >>= 7;
node_ids[(*node_ids_size)++] = v ? (byte | 0x80) : byte;
} while (v);
uint8_t byte = zz & 0x7f;
zz >>= 7;
node_ids[(*node_ids_size)++] = zz ? (byte | 0x80) : byte;
} while (zz);
}
#endif
@@ -2786,6 +2788,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
#ifdef USE_ISEQ_NODE_ID
uint8_t *node_ids = ALLOC_N(uint8_t, (size_t)insn_num * 5);
unsigned int node_ids_size = 0;
int prev_node_id = 0;
#endif
if (ISEQ_IS_SIZE(body)) {
body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body));
@@ -2965,7 +2968,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
#ifdef USE_ISEQ_NODE_ID
add_node_id(node_ids, &node_ids_size, iobj->insn_info.node_id);
add_node_id(node_ids, &node_ids_size, iobj->insn_info.node_id, &prev_node_id);
#endif
code_index += len;
break;
@@ -3003,7 +3006,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
#ifdef USE_ISEQ_NODE_ID
add_node_id(node_ids, &node_ids_size, -1);
add_node_id(node_ids, &node_ids_size, -1, &prev_node_id);
#endif
}
if (diff > 1) {
@@ -12814,7 +12817,7 @@ typedef uint32_t ibf_offset_t;
#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
#ifdef RUBY_DEVEL
#define IBF_DEVEL_VERSION 11
#define IBF_DEVEL_VERSION 12
#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
#else
#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
+10 -8
View File
@@ -2581,16 +2581,16 @@ rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
static int
node_id_stream_read(const uint8_t **ptr, const uint8_t *end)
{
uint32_t v = 0;
uint32_t zz = 0;
unsigned int shift = 0;
while (*ptr < end) {
uint8_t byte = **ptr;
(*ptr)++;
v |= (uint32_t)(byte & 0x7f) << shift;
zz |= (uint32_t)(byte & 0x7f) << shift;
if (!(byte & 0x80)) break;
shift += 7;
}
return (int)v - 1;
return (int)(zz >> 1) ^ -(int)(zz & 1);
}
int
@@ -2605,10 +2605,11 @@ rb_iseq_node_id(const rb_iseq_t *iseq, size_t pos)
const uint8_t *ptr = body->insns_info.node_ids;
const uint8_t *end = ptr + body->insns_info.node_ids_size;
size_t code_pos = 0;
int node_id = 0;
while (ptr < end && code_pos < body->iseq_size) {
size_t len = (size_t)insn_len(code[code_pos]);
int node_id = node_id_stream_read(&ptr, end);
node_id += node_id_stream_read(&ptr, end);
if (pos < code_pos + len) {
return node_id;
}
@@ -3798,10 +3799,9 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
prev_insn_info = NULL;
#ifdef USE_ISEQ_NODE_ID
VALUE node_ids = rb_ary_new();
/* The node id stream holds one varint per instruction, in the same
* order as the instructions in nbody. */
const uint8_t *nid_ptr = iseq_body->insns_info.node_ids;
const uint8_t *nid_end = nid_ptr ? nid_ptr + iseq_body->insns_info.node_ids_size : NULL;
int node_id = 0;
#endif
for (l=0, pos=0; l<RARRAY_LEN(nbody); l++) {
@@ -3816,8 +3816,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
info = get_insn_info(iseq, pos);
#ifdef USE_ISEQ_NODE_ID
VM_ASSERT(nid_ptr && nid_ptr < nid_end);
rb_ary_push(node_ids, INT2FIX(nid_ptr && nid_ptr < nid_end ?
node_id_stream_read(&nid_ptr, nid_end) : -1));
if (nid_ptr && nid_ptr < nid_end) {
node_id += node_id_stream_read(&nid_ptr, nid_end);
}
rb_ary_push(node_ids, INT2FIX(node_id));
#endif
if (prev_insn_info != info) {