Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base: master
...
compare: chromatic/early_pmc_gc
Checking mergeability… Don't worry, you can still create the pull request.
  • 5 commits
  • 2 files changed
  • 0 commit comments
  • 2 contributors
Commits on Jul 02, 2012
@chromatic chromatic Added early GC PMC reclamation to GC GMS.
This makes the "sweep 0" opcode reclaim all PMCs in the youngest generation
which have their early GC flag set. Set this with the "needs_destroy" opcode.

If you don't know what you're doing, you can cause great harm. If you do know
what you're doing, you can force a cheap GC run every few thousand PMC
allocations to reclaim PMCs you know won't be used ever again without running a
full mark and sweep or waiting to bump up against a memory limit.

This comes with the appropriate warning: YOU are performing manual escape
analysis. If you get that wrong, you'd better have a spectacular test suite to
demonstrate that you have it wrong.
2cd0e7e
@chromatic chromatic Ran headerizer on new lazy GC code. 7ec2caf
@chromatic chromatic Added documentation to new GC GMS functions. 8047502
@chromatic chromatic Added headerization. 1742672
Commits on Nov 02, 2012
@cotto cotto Merge branch 'master' into chromatic/early_pmc_gc c1ae0f2
Showing with 110 additions and 16 deletions.
  1. +107 −13 src/gc/gc_gms.c
  2. +3 −3 t/op/gc.t
View
120 src/gc/gc_gms.c
@@ -285,6 +285,18 @@ static size_t gc_gms_count_used_string_memory(PARROT_INTERP,
__attribute__nonnull__(1)
__attribute__nonnull__(2);
+static void gc_gms_destroy_pmc(PARROT_INTERP,
+ ARGMOD(MarkSweep_GC *self),
+ ARGMOD(PMC *pmc),
+ ARGMOD(pmc_alloc_struct *ptr))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2)
+ __attribute__nonnull__(3)
+ __attribute__nonnull__(4)
+ FUNC_MODIFIES(*self)
+ FUNC_MODIFIES(*pmc)
+ FUNC_MODIFIES(*ptr);
+
static void gc_gms_finalize(PARROT_INTERP)
__attribute__nonnull__(1);
@@ -415,6 +427,12 @@ static void gc_gms_reallocate_string_storage(PARROT_INTERP,
__attribute__nonnull__(1)
__attribute__nonnull__(2);
+static void gc_gms_reclaim_early_gc_pmcs(PARROT_INTERP,
+ ARGMOD(MarkSweep_GC *self))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2)
+ FUNC_MODIFIES(*self);
+
static void gc_gms_seal_object(PARROT_INTERP, ARGIN(PMC *pmc))
__attribute__nonnull__(1)
__attribute__nonnull__(2);
@@ -502,6 +520,11 @@ static int gen2flags(int gen);
__attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp) \
, PARROT_ASSERT_ARG(list))
+#define ASSERT_ARGS_gc_gms_destroy_pmc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(self) \
+ , PARROT_ASSERT_ARG(pmc) \
+ , PARROT_ASSERT_ARG(ptr))
#define ASSERT_ARGS_gc_gms_finalize __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp))
#define ASSERT_ARGS_gc_gms_free_buffer_header __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
@@ -577,6 +600,9 @@ static int gen2flags(int gen);
__attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp) \
, PARROT_ASSERT_ARG(str))
+#define ASSERT_ARGS_gc_gms_reclaim_early_gc_pmcs __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+ PARROT_ASSERT_ARG(interp) \
+ , PARROT_ASSERT_ARG(self))
#define ASSERT_ARGS_gc_gms_seal_object __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp) \
, PARROT_ASSERT_ARG(pmc))
@@ -755,6 +781,78 @@ Parrot_gc_gms_init(PARROT_INTERP, ARGIN(Parrot_GC_Init_Args *args))
}
+/*
+
+=item C<static void gc_gms_destroy_pmc(PARROT_INTERP, MarkSweep_GC *self, PMC
+*pmc, pmc_alloc_struct *ptr)>
+
+Destroys a PMC as far as the GMS GC and the rest of Parrot understand that. You
+may recognize some of this code as internals of Parrot_pmc_destroy(). This
+manual inlining is no accident; it's a performance tweak.
+
+*/
+
+static void
+gc_gms_destroy_pmc(PARROT_INTERP, ARGMOD(MarkSweep_GC *self), ARGMOD(PMC *pmc),
+ ARGMOD(pmc_alloc_struct *ptr))
+{
+ ASSERT_ARGS(gc_gms_destroy_pmc)
+
+ if (PObj_custom_destroy_TEST(pmc))
+ VTABLE_destroy(interp, pmc);
+
+ if (pmc->vtable->attr_size && PMC_data(pmc))
+ Parrot_gc_free_pmc_attributes(interp, pmc);
+ PMC_data(pmc) = NULL;
+
+ PObj_on_free_list_SET(pmc);
+ PObj_gc_CLEAR(pmc);
+
+ Parrot_gc_pool_free(interp, self->pmc_allocator, ptr);
+}
+
+/*
+
+=item C<static void gc_gms_reclaim_early_gc_pmcs(PARROT_INTERP, MarkSweep_GC
+*self)>
+
+Reclaims all PMCs in the youngest generation which have been marked for early
+GC. This will remove any such PMCs found from the youngest generation. This
+does not perform a full mark and sweep. This only searches the youngest
+generation. This stops after it's reached the end of that generation or it's
+found the number of early GC PMCs it expects.
+
+*/
+
+static void
+gc_gms_reclaim_early_gc_pmcs(PARROT_INTERP, ARGMOD(MarkSweep_GC *self))
+{
+ ASSERT_ARGS(gc_gms_reclaim_early_gc_pmcs)
+ /* assumption: early GC PMCs are all in the youngest generation */
+ const Parrot_Pointer_Array *gen = self->objects[ self->youngest_child ];
+
+ POINTER_ARRAY_ITER(gen,
+ pmc_alloc_struct * const item = (pmc_alloc_struct *)ptr;
+ PMC * const pmc = &(item->pmc);
+
+ /* remove only early gc objects */
+ if (PObj_needs_early_gc_TEST(pmc)) {
+ /* remove PMC from this generation */
+ Parrot_pa_remove(interp, gen, ptr);
+ gc_gms_destroy_pmc(interp, self, pmc, ptr);
+
+ /* and do some bookkeeping */
+ self->num_early_gc_PMCs--;
+ interp->gc_sys->stats.memory_used -= sizeof (PMC);
+
+ /* and don't do more work than necessary */
+ if (self->num_early_gc_PMCs == 0)
+ return;
+ });
+
+ return;
+}
+
static void
gc_gms_mark_and_sweep(PARROT_INTERP, UINTVAL flags)
{
@@ -783,6 +881,13 @@ gc_gms_mark_and_sweep(PARROT_INTERP, UINTVAL flags)
gc_gms_print_stats(interp, "Before");
gc_gms_check_sanity(interp);
+
+ /* handle PMCs on the early GC list */
+ if (flags & GC_lazy_FLAG) {
+ gc_gms_reclaim_early_gc_pmcs(interp, self);
+ return;
+ }
+
/*
2. Choose K - how many collections we want to collect. Collections [0..K]
will be collected. Remember K in C<self->gen_to_collect>.
@@ -1085,19 +1190,7 @@ gc_gms_sweep_pools(PARROT_INTERP, ARGMOD(MarkSweep_GC *self))
Parrot_pa_remove(interp, self->objects[i], item->ptr);
interp->gc_sys->stats.memory_used -= sizeof (PMC);
-
- /* this is manual inlining of Parrot_pmc_destroy() */
- if (PObj_custom_destroy_TEST(pmc))
- VTABLE_destroy(interp, pmc);
-
- if (pmc->vtable->attr_size && PMC_data(pmc))
- Parrot_gc_free_pmc_attributes(interp, pmc);
- PMC_data(pmc) = NULL;
-
- PObj_on_free_list_SET(pmc);
- PObj_gc_CLEAR(pmc);
-
- Parrot_gc_pool_free(interp, self->pmc_allocator, ptr);
+ gc_gms_destroy_pmc(interp, self, pmc, ptr);
});
POINTER_ARRAY_ITER(self->strings[i],
@@ -1913,6 +2006,7 @@ gc_gms_pmc_needs_early_collection(PARROT_INTERP, ARGMOD(PMC *pmc))
ASSERT_ARGS(gc_gms_pmc_needs_early_collection)
MarkSweep_GC * const self = (MarkSweep_GC *)interp->gc_sys->gc_private;
++self->num_early_gc_PMCs;
+ PObj_needs_early_gc_SET(pmc);
}
/*
View
6 t/op/gc.t
@@ -86,13 +86,13 @@ GC related bugs.
$I1 = interpinfo .INTERPINFO_GC_MARK_RUNS # How many GC mark runs have we done already?
$P0 = new 'Undef' #kill object
sweep 0
- $I2 = interpinfo .INTERPINFO_GC_MARK_RUNS # Should be one more now
+ $I2 = interpinfo .INTERPINFO_GC_MARK_RUNS # Should be no more now
$I3 = $I2 - $I1
sweep 0
$I4 = interpinfo .INTERPINFO_GC_MARK_RUNS # Should be same as last
$I5 = $I4 - $I2
- is($I3,1, "sweep_0_need_destroy_destroy_obj")
- is($I5,0, "sweep_0_need_destroy_destroy_obj")
+ is($I3, 0, "sweep_0_need_destroy_destroy_obj")
+ is($I5, 0, "sweep_0_need_destroy_destroy_obj")
.end

No commit comments for this range

Something went wrong with that request. Please try again.