Skip to content

Commit 41e06e4

Browse files
d-nettokpamnany
authored andcommitted
Backport promote objects more eagerly (JuliaLang#49644) (#80)
1 parent 581cd08 commit 41e06e4

File tree

5 files changed

+24
-117
lines changed

5 files changed

+24
-117
lines changed

src/gc-debug.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -546,14 +546,6 @@ JL_NO_ASAN static void gc_scrub_range(char *low, char *high)
546546
// Make sure the sweep rebuild the freelist
547547
pg->has_marked = 1;
548548
pg->has_young = 1;
549-
// Find the age bit
550-
char *page_begin = gc_page_data(tag) + GC_PAGE_OFFSET;
551-
int obj_id = (((char*)tag) - page_begin) / osize;
552-
uint8_t *ages = pg->ages + obj_id / 8;
553-
// Force this to be a young object to save some memory
554-
// (especially on 32bit where it's more likely to have pointer-like
555-
// bit patterns)
556-
*ages &= ~(1 << (obj_id % 8));
557549
memset(tag, 0xff, osize);
558550
// set mark to GC_MARKED (young and marked)
559551
tag->bits.gc = GC_MARKED;

src/gc-pages.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT
178178
// return a page to the freemap allocator
179179
void jl_gc_free_page(jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT
180180
{
181-
free(pg->ages);
182181
void *p = pg->data;
183182
gc_alloc_map_set((char*)p, GC_PAGE_FREED);
184183
// tell the OS we don't need these pages right now

src/gc.c

Lines changed: 22 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ static int mark_reset_age = 0;
803803
*
804804
* <-[(quick)sweep]-
805805
* |
806-
* ----> GC_OLD <--[(quick)sweep && age>promotion]--
806+
* ----> GC_OLD <--[(quick)sweep]-------------------
807807
* | | |
808808
* | | GC_MARKED (in remset) |
809809
* | | ^ | |
@@ -820,9 +820,9 @@ static int mark_reset_age = 0;
820820
* ========= above this line objects are old ========= |
821821
* |
822822
* ----[new]------> GC_CLEAN ------[mark]-----------> GC_MARKED
823-
* | ^ |
824-
* <-[(quick)sweep]--- | |
825-
* --[(quick)sweep && age<=promotion]---
823+
* |
824+
* <-[(quick)sweep]---
825+
*
826826
*/
827827

828828
// A quick sweep is a sweep where `!sweep_full`
@@ -836,20 +836,11 @@ static int mark_reset_age = 0;
836836
// When a write barrier triggers, the offending marked object is both queued,
837837
// so as not to trigger the barrier again, and put in the remset.
838838

839-
840-
#define PROMOTE_AGE 1
841-
// this cannot be increased as is without changing :
842-
// - sweep_page which is specialized for 1bit age
843-
// - the size of the age storage in jl_gc_pagemeta_t
844-
845-
846839
static int64_t scanned_bytes; // young bytes scanned while marking
847840
static int64_t perm_scanned_bytes; // old bytes scanned while marking
848841
int prev_sweep_full = 1;
849842
int current_sweep_full = 0;
850843

851-
#define inc_sat(v,s) v = (v) >= s ? s : (v)+1
852-
853844
// Full collection heuristics
854845
static int64_t pool_live_bytes = 0;
855846
static int64_t live_bytes = 0;
@@ -954,9 +945,8 @@ STATIC_INLINE void gc_setmark_big(jl_ptls_t ptls, jl_taggedvalue_t *o,
954945
// We can't easily tell if the object is old or being promoted
955946
// from the gc bits but if the `age` is `0` then the object
956947
// must be already on a young list.
957-
if (mark_reset_age && hdr->age) {
948+
if (mark_reset_age) {
958949
// Reset the object as if it was just allocated
959-
hdr->age = 0;
960950
gc_queue_big_marked(ptls, hdr, 1);
961951
}
962952
}
@@ -981,10 +971,6 @@ STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o,
981971
ptls->gc_cache.scanned_bytes += page->osize;
982972
if (mark_reset_age) {
983973
page->has_young = 1;
984-
char *page_begin = gc_page_data(o) + GC_PAGE_OFFSET;
985-
int obj_id = (((char*)o) - page_begin) / page->osize;
986-
uint8_t *ages = page->ages + obj_id / 8;
987-
jl_atomic_fetch_and_relaxed((_Atomic(uint8_t)*)ages, ~(1 << (obj_id % 8)));
988974
}
989975
}
990976
objprofile_count(jl_typeof(jl_valueof(o)),
@@ -1035,37 +1021,6 @@ void gc_setmark_buf(jl_ptls_t ptls, void *o, uint8_t mark_mode, size_t minsz) JL
10351021
gc_setmark_buf_(ptls, o, mark_mode, minsz);
10361022
}
10371023

1038-
void jl_gc_force_mark_old(jl_ptls_t ptls, jl_value_t *v) JL_NOTSAFEPOINT
1039-
{
1040-
jl_taggedvalue_t *o = jl_astaggedvalue(v);
1041-
jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(v);
1042-
size_t dtsz = jl_datatype_size(dt);
1043-
if (o->bits.gc == GC_OLD_MARKED)
1044-
return;
1045-
o->bits.gc = GC_OLD_MARKED;
1046-
if (dt == jl_simplevector_type) {
1047-
size_t l = jl_svec_len(v);
1048-
dtsz = l * sizeof(void*) + sizeof(jl_svec_t);
1049-
}
1050-
else if (dt->name == jl_array_typename) {
1051-
jl_array_t *a = (jl_array_t*)v;
1052-
if (!a->flags.pooled)
1053-
dtsz = GC_MAX_SZCLASS + 1;
1054-
}
1055-
else if (dt == jl_module_type) {
1056-
dtsz = sizeof(jl_module_t);
1057-
}
1058-
else if (dt == jl_task_type) {
1059-
dtsz = sizeof(jl_task_t);
1060-
}
1061-
else if (dt == jl_symbol_type) {
1062-
return;
1063-
}
1064-
gc_setmark(ptls, o, GC_OLD_MARKED, dtsz);
1065-
if (dt->layout->npointers != 0)
1066-
jl_gc_queue_root(v);
1067-
}
1068-
10691024
STATIC_INLINE void maybe_collect(jl_ptls_t ptls)
10701025
{
10711026
if (jl_atomic_load_relaxed(&ptls->gc_num.allocd) >= 0 || jl_gc_debug_check_other()) {
@@ -1161,7 +1116,6 @@ STATIC_INLINE jl_value_t *jl_gc_big_alloc_inner(jl_ptls_t ptls, size_t sz)
11611116
memset(v, 0xee, allocsz);
11621117
#endif
11631118
v->sz = allocsz;
1164-
v->age = 0;
11651119
gc_big_object_link(v, &ptls->heap.big_objects);
11661120
return jl_valueof(&v->header);
11671121
}
@@ -1199,16 +1153,8 @@ static bigval_t **sweep_big_list(int sweep_full, bigval_t **pv) JL_NOTSAFEPOINT
11991153
int old_bits = bits;
12001154
if (gc_marked(bits)) {
12011155
pv = &v->next;
1202-
int age = v->age;
1203-
if (age >= PROMOTE_AGE || bits == GC_OLD_MARKED) {
1204-
if (sweep_full || bits == GC_MARKED) {
1205-
bits = GC_OLD;
1206-
}
1207-
}
1208-
else {
1209-
inc_sat(age, PROMOTE_AGE);
1210-
v->age = age;
1211-
bits = GC_CLEAN;
1156+
if (sweep_full || bits == GC_MARKED) {
1157+
bits = GC_OLD;
12121158
}
12131159
v->bits.gc = bits;
12141160
}
@@ -1386,14 +1332,13 @@ STATIC_INLINE jl_taggedvalue_t *gc_reset_page(jl_ptls_t ptls2, const jl_gc_pool_
13861332
assert(GC_PAGE_OFFSET >= sizeof(void*));
13871333
pg->nfree = (GC_PAGE_SZ - GC_PAGE_OFFSET) / p->osize;
13881334
pg->pool_n = p - ptls2->heap.norm_pools;
1389-
memset(pg->ages, 0, GC_PAGE_SZ / 8 / p->osize + 1);
13901335
jl_taggedvalue_t *beg = (jl_taggedvalue_t*)(pg->data + GC_PAGE_OFFSET);
13911336
pg->has_young = 0;
13921337
pg->has_marked = 0;
1393-
pg->fl_begin_offset = -1;
1394-
pg->fl_end_offset = -1;
13951338
pg->prev_nold = 0;
13961339
pg->nold = 0;
1340+
pg->fl_begin_offset = UINT16_MAX;
1341+
pg->fl_end_offset = UINT16_MAX;
13971342
return beg;
13981343
}
13991344

@@ -1416,7 +1361,6 @@ static NOINLINE jl_taggedvalue_t *gc_add_page(jl_gc_pool_t *p) JL_NOTSAFEPOINT
14161361
pg = jl_gc_alloc_page();
14171362
}
14181363
pg->osize = p->osize;
1419-
pg->ages = (uint8_t*)malloc_s(GC_PAGE_SZ / 8 / p->osize + 1);
14201364
pg->thread_n = ptls->tid;
14211365
set_page_metadata(pg);
14221366
push_lf_back(&ptls->page_metadata_allocd, pg);
@@ -1524,7 +1468,6 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
15241468
jl_gc_pagemeta_t *pg, int osize) JL_NOTSAFEPOINT
15251469
{
15261470
char *data = pg->data;
1527-
uint8_t *ages = pg->ages;
15281471
jl_taggedvalue_t *v = (jl_taggedvalue_t*)(data + GC_PAGE_OFFSET);
15291472
char *lim = data + GC_PAGE_SZ - osize;
15301473
char *lim_newpages = data + GC_PAGE_SZ;
@@ -1572,43 +1515,25 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
15721515
jl_taggedvalue_t *fl = NULL;
15731516
jl_taggedvalue_t **pfl = &fl;
15741517
jl_taggedvalue_t **pfl_begin = NULL;
1575-
uint8_t msk = 1; // mask for the age bit in the current age byte
15761518
while ((char*)v <= lim) {
15771519
int bits = v->bits.gc;
15781520
// if an object is past `lim_newpages` then we can guarantee it's garbage
15791521
if (!gc_marked(bits) || (char*)v >= lim_newpages) {
15801522
*pfl = v;
15811523
pfl = &v->next;
1582-
pfl_begin = pfl_begin ? pfl_begin : pfl;
1524+
pfl_begin = (pfl_begin != NULL) ? pfl_begin : pfl;
15831525
pg_nfree++;
1584-
*ages &= ~msk;
15851526
}
15861527
else { // marked young or old
1587-
if (*ages & msk || bits == GC_OLD_MARKED) { // old enough
1588-
// `!age && bits == GC_OLD_MARKED` is possible for
1589-
// non-first-class objects like `jl_binding_t`
1590-
if (current_sweep_full || bits == GC_MARKED) {
1591-
bits = v->bits.gc = GC_OLD; // promote
1592-
}
1593-
prev_nold++;
1594-
}
1595-
else {
1596-
assert(bits == GC_MARKED);
1597-
bits = v->bits.gc = GC_CLEAN; // unmark
1598-
has_young = 1;
1528+
if (current_sweep_full || bits == GC_MARKED) { // old enough
1529+
bits = v->bits.gc = GC_OLD; // promote
15991530
}
1531+
prev_nold++;
16001532
has_marked |= gc_marked(bits);
1601-
*ages |= msk;
16021533
freedall = 0;
16031534
}
16041535
v = (jl_taggedvalue_t*)((char*)v + osize);
1605-
msk <<= 1;
1606-
if (!msk) {
1607-
msk = 1;
1608-
ages++;
1609-
}
16101536
}
1611-
16121537
assert(!freedall);
16131538
pg->has_marked = has_marked;
16141539
pg->has_young = has_young;
@@ -1617,8 +1542,8 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag
16171542
pg->fl_end_offset = (char*)pfl - data;
16181543
}
16191544
else {
1620-
pg->fl_begin_offset = -1;
1621-
pg->fl_end_offset = -1;
1545+
pg->fl_begin_offset = UINT16_MAX;
1546+
pg->fl_end_offset = UINT16_MAX;
16221547
}
16231548

16241549
pg->nfree = pg_nfree;
@@ -1668,7 +1593,7 @@ static void gc_sweep_other(jl_ptls_t ptls, int sweep_full) JL_NOTSAFEPOINT
16681593

16691594
static void gc_pool_sync_nfree(jl_gc_pagemeta_t *pg, jl_taggedvalue_t *last) JL_NOTSAFEPOINT
16701595
{
1671-
assert(pg->fl_begin_offset != (uint16_t)-1);
1596+
assert(pg->fl_begin_offset != UINT16_MAX);
16721597
char *cur_pg = gc_page_data(last);
16731598
// Fast path for page that has no allocation
16741599
jl_taggedvalue_t *fl_beg = (jl_taggedvalue_t*)(cur_pg + pg->fl_begin_offset);
@@ -1777,7 +1702,7 @@ static void gc_sweep_pool(void)
17771702
pfl[t_i * JL_GC_N_POOLS + i] = &p->freelist;
17781703

17791704
last = p->newpages;
1780-
if (last) {
1705+
if (last != NULL) {
17811706
char *last_p = (char*)last;
17821707
jl_gc_pagemeta_t *pg = jl_assume(page_metadata_unsafe(last_p - 1));
17831708
assert(last_p - gc_page_data(last_p - 1) >= GC_PAGE_OFFSET);
@@ -4010,7 +3935,6 @@ jl_value_t *jl_gc_realloc_string(jl_value_t *s, size_t sz)
40103935
// old pointer.
40113936
bigval_t *newbig = (bigval_t*)gc_managed_realloc_(ptls, hdr, allocsz, oldsz, 1, s, 0);
40123937
newbig->sz = allocsz;
4013-
newbig->age = 0;
40143938
gc_big_object_link(newbig, &ptls->heap.big_objects);
40153939
jl_value_t *snew = jl_valueof(&newbig->header);
40163940
*(size_t*)snew = sz;
@@ -4184,7 +4108,7 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
41844108
{
41854109
p = (char *) p - 1;
41864110
jl_gc_pagemeta_t *meta = page_metadata(p);
4187-
if (meta && meta->ages) {
4111+
if (meta) {
41884112
char *page = gc_page_data(p);
41894113
// offset within page.
41904114
size_t off = (char *)p - page;
@@ -4219,7 +4143,7 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
42194143
char *data = gc_page_data(newpages);
42204144
if (data != meta->data) {
42214145
// Pages on newpages form a linked list where only the
4222-
// first one is allocated from (see reset_page()).
4146+
// first one is allocated from (see gc_reset_page()).
42234147
// All other pages are empty.
42244148
return NULL;
42254149
}
@@ -4247,19 +4171,16 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
42474171
// entries and 1 for live objects. The above subcases arise
42484172
// because allocating a cell will not update the age bit, so we
42494173
// need extra logic for pages that have been allocated from.
4250-
unsigned obj_id = (off - off2) / osize;
42514174
// We now distinguish between the second and third subcase.
42524175
// Freelist entries are consumed in ascending order. Anything
42534176
// before the freelist pointer was either live during the last
42544177
// sweep or has been allocated since.
42554178
if (gc_page_data(cell) == gc_page_data(pool->freelist)
42564179
&& (char *)cell < (char *)pool->freelist)
42574180
goto valid_object;
4258-
// We know now that the age bit reflects liveness status during
4259-
// the last sweep and that the cell has not been reused since.
4260-
if (!(meta->ages[obj_id / 8] & (1 << (obj_id % 8)))) {
4261-
return NULL;
4262-
}
4181+
// already skipped marked or old objects above, so here
4182+
// the age bits are 0, thus the object is on the freelist
4183+
return NULL;
42634184
// Not a freelist entry, therefore a valid object.
42644185
valid_object:
42654186
// We have to treat objects with type `jl_buff_tag` differently,

src/gc.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,7 @@ typedef struct _jl_gc_chunk_t {
118118
JL_EXTENSION typedef struct _bigval_t {
119119
struct _bigval_t *next;
120120
struct _bigval_t **prev; // pointer to the next field of the prev entry
121-
union {
122-
size_t sz;
123-
uintptr_t age : 2;
124-
};
121+
size_t sz;
125122
#ifdef _P64 // Add padding so that the value is 64-byte aligned
126123
// (8 pointers of 8 bytes each) - (4 other pointers in struct)
127124
void *_padding[8 - 4];
@@ -177,12 +174,11 @@ typedef struct _jl_gc_pagemeta_t {
177174
// number of free objects in this page.
178175
// invalid if pool that owns this page is allocating objects from this page.
179176
uint16_t nfree;
180-
uint16_t osize; // size of each object in this page
177+
uint16_t osize; // size of each object in this page
181178
uint16_t fl_begin_offset; // offset of first free object in this page
182179
uint16_t fl_end_offset; // offset of last free object in this page
183180
uint16_t thread_n; // thread id of the heap that owns this page
184181
char *data;
185-
uint8_t *ages;
186182
} jl_gc_pagemeta_t;
187183

188184
extern jl_gc_page_stack_t global_page_pool_lazily_freed;

src/julia_internal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ void *jl_gc_perm_alloc_nolock(size_t sz, int zero,
337337
unsigned align, unsigned offset) JL_NOTSAFEPOINT;
338338
void *jl_gc_perm_alloc(size_t sz, int zero,
339339
unsigned align, unsigned offset) JL_NOTSAFEPOINT;
340-
void jl_gc_force_mark_old(jl_ptls_t ptls, jl_value_t *v);
341340
void gc_sweep_sysimg(void);
342341

343342

0 commit comments

Comments
 (0)