Implement Write Barriers on IO::Buffer

Benchmark:

```
require "benchmark"

puts(Benchmark.measure do
  ary = 1_000_000.times.map { IO::Buffer.for("") }

  10.times { GC.start(full_mark: false) }
end)
```

Before:

```
14.330119   0.051497  14.381616 ( 14.445106)
```

After:

```
7.481152   0.040166   7.521318 (  7.535209)
```
This commit is contained in:
Peter Zhu 2023-12-13 14:09:27 -05:00
parent 157e6c8a51
commit c5e3d6da9c

View File

@ -183,7 +183,7 @@ io_buffer_zero(struct rb_io_buffer *buffer)
} }
static void static void
io_buffer_initialize(struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source) io_buffer_initialize(VALUE self, struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
{ {
if (base) { if (base) {
// If we are provided a pointer, we use it. // If we are provided a pointer, we use it.
@ -209,7 +209,7 @@ io_buffer_initialize(struct rb_io_buffer *buffer, void *base, size_t size, enum
buffer->base = base; buffer->base = base;
buffer->size = size; buffer->size = size;
buffer->flags = flags; buffer->flags = flags;
buffer->source = source; RB_OBJ_WRITE(self, &buffer->source, source);
} }
static int static int
@ -286,7 +286,7 @@ static const rb_data_type_t rb_io_buffer_type = {
.dsize = rb_io_buffer_type_size, .dsize = rb_io_buffer_type_size,
}, },
.data = NULL, .data = NULL,
.flags = RUBY_TYPED_FREE_IMMEDIATELY, .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
}; };
// Extract an offset argument, which must be a positive integer. // Extract an offset argument, which must be a positive integer.
@ -420,7 +420,7 @@ static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_b
if (!(flags & RB_IO_BUFFER_READONLY)) if (!(flags & RB_IO_BUFFER_READONLY))
rb_str_modify(string); rb_str_modify(string);
io_buffer_initialize(buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string); io_buffer_initialize(instance, buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
return instance; return instance;
} }
@ -555,7 +555,7 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
struct rb_io_buffer *buffer = NULL; struct rb_io_buffer *buffer = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer); TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
io_buffer_initialize(buffer, base, size, flags, Qnil); io_buffer_initialize(instance, buffer, base, size, flags, Qnil);
return instance; return instance;
} }
@ -720,7 +720,7 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
flags |= io_flags_for_size(size); flags |= io_flags_for_size(size);
} }
io_buffer_initialize(buffer, NULL, size, flags, Qnil); io_buffer_initialize(self, buffer, NULL, size, flags, Qnil);
return self; return self;
} }
@ -1278,10 +1278,12 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
slice->size = length; slice->size = length;
// The source should be the root buffer: // The source should be the root buffer:
if (buffer->source != Qnil) if (buffer->source != Qnil) {
slice->source = buffer->source; RB_OBJ_WRITE(instance, &slice->source, buffer->source);
else }
slice->source = self; else {
RB_OBJ_WRITE(instance, &slice->source, self);
}
return instance; return instance;
} }
@ -1478,11 +1480,11 @@ io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
} }
static void static void
io_buffer_resize_copy(struct rb_io_buffer *buffer, size_t size) io_buffer_resize_copy(VALUE self, struct rb_io_buffer *buffer, size_t size)
{ {
// Slow path: // Slow path:
struct rb_io_buffer resized; struct rb_io_buffer resized;
io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil); io_buffer_initialize(self, &resized, NULL, size, io_flags_for_size(size), Qnil);
if (buffer->base) { if (buffer->base) {
size_t preserve = buffer->size; size_t preserve = buffer->size;
@ -1507,7 +1509,7 @@ rb_io_buffer_resize(VALUE self, size_t size)
} }
if (buffer->base == NULL) { if (buffer->base == NULL) {
io_buffer_initialize(buffer, NULL, size, io_flags_for_size(size), Qnil); io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
return; return;
} }
@ -1552,7 +1554,7 @@ rb_io_buffer_resize(VALUE self, size_t size)
return; return;
} }
io_buffer_resize_copy(buffer, size); io_buffer_resize_copy(self, buffer, size);
} }
/* /*
@ -2247,7 +2249,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size); rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
io_buffer_initialize(buffer, NULL, source_size, io_flags_for_size(source_size), Qnil); io_buffer_initialize(self, buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL); return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
} }