Skip to content

Commit e1ac393

Browse files
committed
Added some more comments and refactored slightly.
1 parent f0d6fcb commit e1ac393

File tree

2 files changed

+21
-2
lines changed

2 files changed

+21
-2
lines changed

ruby/ext/google/protobuf_c/protobuf.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ VALUE secondary_map_mutex = Qnil;
260260
// Lambda that will GC entries from the secondary map that are no longer present
261261
// in the primary map.
262262
VALUE gc_secondary_map = Qnil;
263+
ID length;
263264

264265
extern VALUE weak_obj_cache;
265266

@@ -273,14 +274,30 @@ static void SecondaryMap_Init() {
273274
" secondary.delete_if { |k, v| !weak.key?(v) }\n"
274275
"}\n");
275276
secondary_map_mutex = rb_mutex_new();
277+
length = rb_intern("length");
276278
}
277279

280+
// The secondary map is a regular Hash, and will never shrink on its own.
281+
// The main object cache is a WeakMap that will automatically remove entries
282+
// when the target object is no longer reachable, but unless we manually
283+
// remove the corresponding entries from the secondary map, it will grow
284+
// without bound.
285+
//
286+
// To avoid this unbounded growth we periodically remove entries from the
287+
// secondary map that are no longer present in the WeakMap. The logic of
288+
// how often to perform this GC is an artbirary tuning parameter that
289+
// represents a straightforward CPU/memory tradeoff.
278290
static void SecondaryMap_MaybeGC() {
279-
size_t weak_len = NUM2ULL(rb_funcall(weak_obj_cache, rb_intern("length"), 0));
291+
size_t weak_len = NUM2ULL(rb_funcall(weak_obj_cache, length, 0));
280292
size_t secondary_len = RHASH_SIZE(secondary_map);
281293
size_t waste = secondary_len - weak_len;
282294
PBRUBY_ASSERT(secondary_len >= weak_len);
283-
if (waste > 1000 && waste > secondary_len * 0.2) {
295+
// GC if we could remove at least 2000 entries or 20% of the table size
296+
// (whichever is greater). Since the cost of the GC pass is O(N), we
297+
// want to make sure that we condition this on overall table size, to
298+
// avoid O(N^2) CPU costs.
299+
size_t threshold = PBRUBY_MAX(secondary_len * 0.2, 2000);
300+
if (waste > threshold) {
284301
rb_funcall(gc_secondary_map, rb_intern("call"), 2, secondary_map, weak_obj_cache);
285302
}
286303
}

ruby/ext/google/protobuf_c/protobuf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ extern VALUE cTypeError;
106106
#define PBRUBY_ASSERT(expr) assert(expr)
107107
#endif
108108

109+
#define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))
110+
109111
#define UPB_UNUSED(var) (void)var
110112

111113
#endif // __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__

0 commit comments

Comments
 (0)