Skip to content

Commit

Permalink
Make compaction available for Oj::Doc
Browse files Browse the repository at this point in the history
Introduce compaction to `fast.c`, only if `rb_gc_mark_movable` is
available. Otherwise, keep a version that supports ruby <2.7.

Since `rb_data_object_wrap` and `TypedData_Wrap_Struct` is available
in every supported versions (eg >=2.4.10), I've also removed support
for earlier versions.

Result for compaction is great. I've tested it by allocating and
removing arbitrary sized json. For a count of 70 pages, we can see
that we used to need 68 pages after compaction,and now 61 pages. Of
course this test include also lots of noise, but if we count pinned
objects (objects that are marked in C extension as uncollectible),
we can see that for a benchmark with no allocations, we have the
same amount as a benchmark with this commit with thousands of allocations
(499 pinned objects total), and before this, `Oj::Doc` used to generate
5k more objects (5499 pinned objects total).

Hence this is a win, see PR for a graphical result.

Signed-off-by: Ulysse Buonomo <buonomo.ulysse@gmail.com>
  • Loading branch information
BuonOmo committed Apr 7, 2021
1 parent 34df54c commit 6e88d14
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG

## Ongoing

- Added support for `GC.compact` on `Oj::Doc`

## 3.11.3 - 2021-03-09

- Fixed `respond_to?` method on `Oj::EasyHash`.
Expand Down
1 change: 0 additions & 1 deletion Rakefile
Expand Up @@ -92,4 +92,3 @@ begin

rescue LoadError
end

3 changes: 2 additions & 1 deletion ext/oj/extconf.rb
Expand Up @@ -26,8 +26,9 @@
have_func('rb_time_timespec')
have_func('rb_ivar_count')
have_func('rb_ivar_foreach')
# Support for compaction.
have_func('rb_gc_mark_movable')
have_func('stpcpy')
have_func('rb_data_object_wrap')
have_func('pthread_mutex_init')

dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
Expand Down
71 changes: 63 additions & 8 deletions ext/oj/fast.c
Expand Up @@ -19,6 +19,13 @@
//#define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
#define BATCH_SIZE 100

// Support for compaction
#ifdef HAVE_RB_GC_MARK_MOVABLE
#define mark rb_gc_mark_movable
#else
#define mark rb_gc_mark
#endif

typedef struct _batch
{
struct _batch *next;
Expand Down Expand Up @@ -921,7 +928,7 @@ mark_leaf(Leaf leaf)
}
break;
case RUBY_VAL:
rb_gc_mark(leaf->value);
mark(leaf->value);
break;

default:
Expand All @@ -936,10 +943,63 @@ mark_doc(void *ptr)
{
Doc doc = (Doc)ptr;

rb_gc_mark(doc->self);
mark(doc->self);
mark_leaf(doc->data);
}
}
#ifdef HAVE_RB_GC_MARK_MOVABLE
static void
compact_leaf(Leaf leaf)
{
switch (leaf->value_type)
{
case COL_VAL:
if (NULL != leaf->elements)
{
Leaf first = leaf->elements->next;
Leaf e = first;

do
{
compact_leaf(e);
e = e->next;
} while (e != first);
}
break;
case RUBY_VAL:
leaf->value = rb_gc_location(leaf->value);
break;

default:
break;
}
}

static void
compact_doc(void *ptr)
{
Doc doc = (Doc)ptr;

if (doc)
{
doc->self = rb_gc_location(doc->self);
compact_leaf(doc->data);
}
}
#endif

static const rb_data_type_t oj_doc_type = {
"Oj/doc",
{
mark_doc,
free_doc_cb,
NULL,
#ifdef HAVE_RB_GC_MARK_MOVABLE
compact_doc,
#endif
},
0, 0,
};

static VALUE
parse_json(VALUE clas, char *json, bool given, bool allocated)
Expand Down Expand Up @@ -988,12 +1048,7 @@ parse_json(VALUE clas, char *json, bool given, bool allocated)
}
}
#endif
// last arg is free func void* func(void*)
#ifdef HAVE_RB_DATA_OBJECT_WRAP
self = rb_data_object_wrap(clas, doc, mark_doc, free_doc_cb);
#else
self = rb_data_object_alloc(clas, doc, mark_doc, free_doc_cb);
#endif
self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
doc->self = self;
doc->json = json;
DATA_PTR(doc->self) = doc;
Expand Down
1 change: 1 addition & 0 deletions lib/oj/bag.rb
@@ -1,3 +1,4 @@
# frozen_string_literal: true

module Oj

Expand Down

0 comments on commit 6e88d14

Please sign in to comment.