|
38 | 38 | #define SINGLE_CHILD(x) (rb_shape_t *)((uintptr_t)x & SINGLE_CHILD_MASK) |
39 | 39 | #define ANCESTOR_CACHE_THRESHOLD 10 |
40 | 40 | #define MAX_SHAPE_ID (SHAPE_BUFFER_SIZE - 1) |
| 41 | +#define ANCESTOR_SEARCH_MAX_DEPTH 2 |
41 | 42 |
|
42 | 43 | static ID id_frozen; |
43 | 44 | static ID id_t_object; |
@@ -723,6 +724,62 @@ rb_shape_transition_shape_capa(rb_shape_t* shape) |
723 | 724 | return rb_shape_transition_shape_capa_create(shape, rb_malloc_grow_capa(shape->capacity, sizeof(VALUE))); |
724 | 725 | } |
725 | 726 |
|
| 727 | +// Same as rb_shape_get_iv_index, but uses a provided valid shape id and index |
| 728 | +// to return a result faster if branches of the shape tree are closely related. |
| 729 | +bool |
| 730 | +rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t *value, shape_id_t *shape_id_hint) |
| 731 | +{ |
| 732 | + attr_index_t index_hint = *value; |
| 733 | + rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id); |
| 734 | + rb_shape_t *initial_shape = shape; |
| 735 | + |
| 736 | + if (*shape_id_hint == INVALID_SHAPE_ID) { |
| 737 | + *shape_id_hint = shape_id; |
| 738 | + return rb_shape_get_iv_index(shape, id, value); |
| 739 | + } |
| 740 | + |
| 741 | + rb_shape_t * shape_hint = rb_shape_get_shape_by_id(*shape_id_hint); |
| 742 | + |
| 743 | + // We assume it's likely shape_id_hint and shape_id have a close common |
| 744 | + // ancestor, so we check up to ANCESTOR_SEARCH_MAX_DEPTH ancestors before |
| 745 | + // eventually using the index, as in case of a match it will be faster. |
| 746 | + // However if the shape doesn't have an index, we walk the entire tree. |
| 747 | + int depth = INT_MAX; |
| 748 | + if (shape->ancestor_index && shape->next_iv_index >= ANCESTOR_CACHE_THRESHOLD) { |
| 749 | + depth = ANCESTOR_SEARCH_MAX_DEPTH; |
| 750 | + } |
| 751 | + |
| 752 | + while (depth > 0 && shape->next_iv_index > index_hint) { |
| 753 | + while (shape_hint->next_iv_index > shape->next_iv_index) { |
| 754 | + shape_hint = rb_shape_get_parent(shape_hint); |
| 755 | + } |
| 756 | + |
| 757 | + if (shape_hint == shape) { |
| 758 | + // We've found a common ancestor so use the index hint |
| 759 | + *value = index_hint; |
| 760 | + *shape_id_hint = rb_shape_id(shape); |
| 761 | + return true; |
| 762 | + } |
| 763 | + if (shape->edge_name == id) { |
| 764 | + // We found the matching id before a common ancestor |
| 765 | + *value = shape->next_iv_index - 1; |
| 766 | + *shape_id_hint = rb_shape_id(shape); |
| 767 | + return true; |
| 768 | + } |
| 769 | + |
| 770 | + shape = rb_shape_get_parent(shape); |
| 771 | + depth--; |
| 772 | + } |
| 773 | + |
| 774 | + // If the original shape had an index but its ancestor doesn't |
| 775 | + // we switch back to the original one as it will be faster. |
| 776 | + if (!shape->ancestor_index && initial_shape->ancestor_index) { |
| 777 | + shape = initial_shape; |
| 778 | + } |
| 779 | + *shape_id_hint = shape_id; |
| 780 | + return rb_shape_get_iv_index(shape, id, value); |
| 781 | +} |
| 782 | + |
726 | 783 | bool |
727 | 784 | rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) |
728 | 785 | { |
|
0 commit comments