Skip to content

Commit 650f9e9

Browse files
committed
Store indices as well to speed up external_blob_index
1 parent 4c75ad0 commit 650f9e9

File tree

2 files changed

+72
-44
lines changed

2 files changed

+72
-44
lines changed

src/gc.c

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ bigval_t *big_objects_marked = NULL;
177177
// See https://algorithmica.org/en/eytzinger
178178
static arraylist_t eytzinger_image_tree;
179179
static arraylist_t eytzinger_idxs;
180-
static uintptr_t gc_img_min = 0ull;
181-
static uintptr_t gc_img_max = ~0ull;
180+
static uintptr_t gc_img_min = (uintptr_t) 0ull;
181+
static uintptr_t gc_img_max = (uintptr_t) ~0ull;
182182

183183
// -- Finalization --
184184
// `ptls->finalizers` and `finalizer_list_marked` might have tagged pointers.
@@ -208,6 +208,34 @@ static int eytzinger(uintptr_t *src, uintptr_t *dest, size_t i, size_t k, size_t
208208
return i;
209209
}
210210

211+
static size_t eyt_obj_idx(jl_value_t *obj) JL_NOTSAFEPOINT {
212+
size_t n = eytzinger_image_tree.len;
213+
if (n == 0) {
214+
return eytzinger_image_tree.len;
215+
}
216+
assert(eytzinger_image_tree.len % 2 == 0 && "Eytzinger tree not even length!");
217+
uintptr_t cmp = (uintptr_t) obj;
218+
if (cmp < gc_img_min || cmp > gc_img_max) {
219+
return eytzinger_image_tree.len;
220+
}
221+
uintptr_t *tree = (uintptr_t*)eytzinger_image_tree.items;
222+
size_t k = 1;
223+
// note that k preserves the history of how we got to the current node
224+
while (k <= n) {
225+
int greater = (cmp > tree[k - 1]);
226+
k <<= 1;
227+
k |= greater;
228+
}
229+
// Free to assume k is nonzero, since we start with k = 1
230+
// This shift does a fast revert of the path until we get
231+
// to a node that evaluated less than cmp.
232+
k >>= (__builtin_ctzll(k) + 1);
233+
assert(k != 0);
234+
assert(k <= n && "Eytzinger tree index out of bounds!");
235+
assert(tree[k - 1] < cmp && "Failed to find lower bound for object!");
236+
return k - 1;
237+
}
238+
211239
//used in staticdata.c after we add an image
212240
void rebuild_image_blob_tree(void) {
213241
size_t orig = eytzinger_image_tree.len;
@@ -227,43 +255,56 @@ void rebuild_image_blob_tree(void) {
227255
eytzinger_idxs.items[i] = (void*)(val + 3 - (i & 1));
228256
}
229257
qsort(eytzinger_idxs.items, eytzinger_idxs.len, sizeof(void*), ptr_cmp);
258+
// for (size_t i = orig; i < eytzinger_idxs.len; i++) {
259+
// jl_safe_printf("idxs[%lu] = %p\n", i, eytzinger_idxs.items[i]);
260+
// }
230261
gc_img_min = (uintptr_t) eytzinger_idxs.items[0];
231262
gc_img_max = (uintptr_t) eytzinger_idxs.items[eytzinger_idxs.len - 1];
232263
eytzinger((uintptr_t*)eytzinger_idxs.items, (uintptr_t*)eytzinger_image_tree.items, 0, 1, eytzinger_idxs.len);
233-
}
234-
235-
static int eyt_obj_in_img(jl_value_t *obj) {
236-
size_t n = eytzinger_image_tree.len;
237-
if (n == 0) {
238-
return 0;
239-
}
240-
assert(eytzinger_image_tree.len % 2 == 0 && "Eytzinger tree not even length!");
241-
uintptr_t cmp = (uintptr_t) obj;
242-
assert(cmp % 4 == 0 && "Object not 4-byte aligned!");
243-
if (cmp < gc_img_min || cmp > gc_img_max) {
264+
// Reuse the scratch memory to store the indices
265+
// Still O(nlogn) because binary search
266+
for (size_t i = 0; i < jl_linkage_blobs.len; i ++) {
267+
uintptr_t val = (uintptr_t) jl_linkage_blobs.items[i];
268+
// This is exactly equal to start + 4/end + 3
269+
uintptr_t cmp = val + 4 - (i & 1);
270+
// This is the same computation as in the prior for loop
271+
uintptr_t eyt_val = val + 3 - (i & 1);
272+
size_t eyt_idx = eyt_obj_idx((jl_value_t*)cmp);
273+
// jl_safe_printf("eyt_idx: %lu, eytzinger_image_tree.len: %lu\n", eyt_idx, eytzinger_image_tree.len);
274+
// jl_safe_printf("val: %p, cmp: %p, eytzinger_image_tree.items[eyt_idx]: %p\n", (void*)val, (void*)cmp, eyt_idx == eytzinger_image_tree.len ? NULL : eytzinger_image_tree.items[eyt_idx]);
275+
assert(((i % 2 == 1) || eytzinger_image_tree.items[eyt_idx] == (void*)eyt_val) && "Eytzinger tree failed to find object!");
276+
assert(((i % 2 == 0) || eytzinger_image_tree.items[eyt_idx] == (void*)eyt_val || cmp == gc_img_max + 1) && "Eytzinger tree failed to find object!");
277+
if (i & 1) {
278+
eytzinger_idxs.items[eyt_idx] = (void*)n_linkage_blobs();
279+
} else {
280+
eytzinger_idxs.items[eyt_idx] = (void*)(i / 2);
281+
}
282+
}
283+
}
284+
285+
static int eyt_obj_in_img(jl_value_t *obj) JL_NOTSAFEPOINT {
286+
assert((uintptr_t) obj % 4 == 0 && "Object not 4-byte aligned!");
287+
int idx = eyt_obj_idx(obj);
288+
if (idx == eytzinger_image_tree.len) {
244289
return 0;
245290
}
246-
uintptr_t *tree = (uintptr_t*)eytzinger_image_tree.items;
247-
size_t k = 1;
248-
// note that k preserves the history of how we got to the current node
249-
while (k <= n) {
250-
int greater = (cmp > tree[k - 1]);
251-
k <<= 1;
252-
k |= greater;
253-
}
254-
// Free to assume k is nonzero, since we start with k = 1
255-
// This shift does a fast revert of the path until we get
256-
// to a node that evaluated less than cmp.
257-
k >>= (__builtin_ctzll(k) + 1);
258-
assert(k != 0);
259-
assert(k <= n && "Eytzinger tree index out of bounds!");
260-
assert(tree[k - 1] < cmp && "Failed to find lower bound for object!");
261-
// Now we use a tiny trick: tree[k - 1] & 1 is whether or not tree[k - 1] is a start or an end
291+
// Now we use a tiny trick: tree[idx] & 1 is whether or not tree[idx] is a start or an end
262292
// of a blob. If it's a start, then the object is in the image, otherwise it's not.
263-
int in_image = (uintptr_t) eytzinger_image_tree.items[k - 1] & 1;
293+
int in_image = (uintptr_t) eytzinger_image_tree.items[idx] & 1;
264294
return in_image;
265295
}
266296

297+
size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT {
298+
assert((uintptr_t) v % 4 == 0 && "Object not 4-byte aligned!");
299+
int eyt_idx = eyt_obj_idx(v);
300+
if (eyt_idx == eytzinger_image_tree.len) {
301+
return n_linkage_blobs();
302+
}
303+
// We fill the invalid slots with the length, so we can just return that
304+
size_t idx = (size_t) eytzinger_idxs.items[eyt_idx];
305+
return idx;
306+
}
307+
267308
uint8_t jl_object_in_image(jl_value_t *obj) JL_NOTSAFEPOINT {
268309
return eyt_obj_in_img(obj);
269310
}

src/julia_internal.h

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -953,20 +953,7 @@ STATIC_INLINE size_t n_linkage_blobs(void) JL_NOTSAFEPOINT
953953
return jl_image_relocs.len;
954954
}
955955

956-
// TODO: Makes this a binary search
957-
STATIC_INLINE size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT {
958-
size_t i, nblobs = n_linkage_blobs();
959-
assert(jl_linkage_blobs.len == 2*nblobs);
960-
for (i = 0; i < nblobs; i++) {
961-
uintptr_t left = (uintptr_t)jl_linkage_blobs.items[2*i];
962-
uintptr_t right = (uintptr_t)jl_linkage_blobs.items[2*i + 1];
963-
if (left < (uintptr_t)v && (uintptr_t)v <= right) {
964-
// the last object may be a singleton (v is shifted by a type tag, so we use exclusive bounds here)
965-
break;
966-
}
967-
}
968-
return i;
969-
}
956+
size_t external_blob_index(jl_value_t *v) JL_NOTSAFEPOINT;
970957

971958
uint8_t jl_object_in_image(jl_value_t* v) JL_NOTSAFEPOINT;
972959

0 commit comments

Comments
 (0)