Investigation: Use writev for response writing
@ahegyi mentioned that Elixir does something pretty interesting which reduces RAM usage quite notably when dealing with large template rendering: https://www.evanmiller.org/elixir-ram-and-the-template-of-doom.html
From my understanding, using writev
means we can pass multiple strings at once to the response IO without first concatenating them into a single big string, which results in more resource usage.
This could provide us some tasty performance gains if we can work it into our stack. This issue is for logging what we know so far about the various components and what we need to look at modifying.
✔ Ruby
writev
is already supported in Ruby by passing multiple strings to IO#write
:
static VALUE
io_write_m(int argc, VALUE *argv, VALUE io)
{
if (argc != 1) {
return io_writev(argc, argv, io);
}
else {
VALUE str = argv[0];
return io_write(io, str, 0);
}
}
✔ Rack
rack
can already take an array of strings as a response:
class HelloWorld
def call(env)
[200, {"Content-Type" => "text/html"}, ["Hello", " World!"]]
end
end
When using Rack::Response
it does use an Array
for buffering: https://github.com/rack/rack/blob/master/lib/rack/response.rb#L263
✅ Puma
Puma is the next step in the chain and where this currently isn't supported. Read the notes below. I've got a draft change which adds support for it in https://github.com/robotmay/puma/compare/master...robotmay:array-buffer
PR open to Puma at https://github.com/puma/puma/pull/2568
✖ Rails
https://api.rubyonrails.org/classes/ActiveSupport/SafeBuffer.html and https://rubydoc.org/docs/rails/6.0.2.1/ActionView/OutputBuffer are the relevant buffers used in template rendering, which are currently backed by a String
. This would need reimplementing to be an Array
.
Still to investigate: does Rails do anything with the output after this point, before it's provided back to Rack?
✖ Hamlit
Hamlit currently uses ActionView::OutputBuffer
as the generator buffer in https://github.com/k0kubun/hamlit/blob/master/lib/hamlit/rails_template.rb.
We would need to change this to use a new buffer backed by an array.