Skip to content

Commit

Permalink
Ruby implement memsize functions for native types
Browse files Browse the repository at this point in the history
Fix: protocolbuffers#10280

This allows Ruby to report a more correct estimation of the
memory used by these objects.

It's useful when running memory profilers against applications.
  • Loading branch information
byroot committed Aug 3, 2022
1 parent 3b456bf commit ab9b741
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 3 deletions.
6 changes: 5 additions & 1 deletion ruby/ext/google/protobuf_c/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ static void Map_mark(void* _self) {
rb_gc_mark(self->arena);
}

static size_t Map_memsize(const void* _self) {
return sizeof(Map);
}

const rb_data_type_t Map_type = {
"Google::Protobuf::Map",
{Map_mark, RUBY_DEFAULT_FREE, NULL},
{Map_mark, RUBY_DEFAULT_FREE, Map_memsize},
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};

Expand Down
6 changes: 5 additions & 1 deletion ruby/ext/google/protobuf_c/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ static void Message_mark(void* _self) {
rb_gc_mark(self->arena);
}

static size_t Message_memsize(const void* _self) {
return sizeof(Message);
}

static rb_data_type_t Message_type = {
"Message",
{Message_mark, RUBY_DEFAULT_FREE, NULL},
{Message_mark, RUBY_DEFAULT_FREE, Message_memsize},
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};

Expand Down
7 changes: 6 additions & 1 deletion ruby/ext/google/protobuf_c/protobuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,16 @@ static void Arena_free(void *data) {
xfree(arena);
}

static size_t Arena_memsize(const void *data) {
const Arena *arena = data;
return _upb_ArenaMemsize(arena->arena) + sizeof(Arena);;
}

static VALUE cArena;

const rb_data_type_t Arena_type = {
"Google::Protobuf::Internal::Arena",
{Arena_mark, Arena_free, NULL},
{Arena_mark, Arena_free, Arena_memsize},
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};

Expand Down
19 changes: 19 additions & 0 deletions ruby/ext/google/protobuf_c/ruby-upb.c
Original file line number Diff line number Diff line change
Expand Up @@ -3349,6 +3349,25 @@ static upb_Arena* arena_findroot(upb_Arena* a) {
return a;
}

size_t _upb_ArenaMemsize(upb_Arena *arena) {
size_t memsize = 0;

mem_block* block = arena->freelist;

while (block) {
memsize += sizeof(mem_block) + block->size;
block = block->next;
}

if (arena->refcount > 1) {
// If other arena were fused we attirbute an equal
// share of memory usage to each one.
memsize /= arena->refcount;
}

return memsize;
}

static void upb_Arena_addblock(upb_Arena* a, upb_Arena* root, void* ptr,
size_t size) {
mem_block* block = ptr;
Expand Down
1 change: 1 addition & 0 deletions ruby/ext/google/protobuf_c/ruby-upb.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ void upb_Arena_Free(upb_Arena* a);
bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func);
bool upb_Arena_Fuse(upb_Arena* a, upb_Arena* b);
void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size);
size_t _upb_ArenaMemsize(upb_Arena *arena);

UPB_INLINE upb_alloc* upb_Arena_Alloc(upb_Arena* a) { return (upb_alloc*)a; }

Expand Down
25 changes: 25 additions & 0 deletions ruby/tests/memory_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/ruby
#
# generated_code.rb is in the same directory as this test.
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))

require 'test/unit'
require 'objspace'
require 'test_import_pb'

class MemoryTest < Test::Unit::TestCase
# 40 byte is the default object size. But the real size is dependent on many things
# such as arch etc, so there's no point trying to assert the exact return value here.
# We merely assert that we return something other than the default.
def test_objspace_memsize_of_arena
assert_operator 40, :<, ObjectSpace.memsize_of(Google::Protobuf::Internal::Arena.new)
end

def test_objspace_memsize_of_message
assert_operator 40, :<, ObjectSpace.memsize_of(FooBar::TestImportedMessage.new)
end

def test_objspace_memsize_of_map
assert_operator 40, :<, ObjectSpace.memsize_of(Google::Protobuf::Map.new(:string, :int32))
end
end

0 comments on commit ab9b741

Please sign in to comment.