From 01a1022857ac29d2dae3eba288fb14ff4815f4d8 Mon Sep 17 00:00:00 2001 From: Brian White Date: Tue, 23 May 2017 23:42:41 -0400 Subject: lib,src: improve writev() performance for Buffers PR-URL: https://github.com/nodejs/node/pull/13187 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Anna Henningsen --- src/stream_base.cc | 150 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 63 deletions(-) (limited to 'src/stream_base.cc') diff --git a/src/stream_base.cc b/src/stream_base.cc index c4b59ee5ca..51bad94a4f 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -100,92 +100,116 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { Local req_wrap_obj = args[0].As(); Local chunks = args[1].As(); + bool all_buffers = args[2]->IsTrue(); - size_t count = chunks->Length() >> 1; + size_t count; + if (all_buffers) + count = chunks->Length(); + else + count = chunks->Length() >> 1; MaybeStackBuffer bufs(count); + uv_buf_t* buf_list = *bufs; - // Determine storage size first size_t storage_size = 0; - for (size_t i = 0; i < count; i++) { - storage_size = ROUND_UP(storage_size, WriteWrap::kAlignSize); - - Local chunk = chunks->Get(i * 2); - - if (Buffer::HasInstance(chunk)) - continue; - // Buffer chunk, no additional storage required - - // String chunk - Local string = chunk->ToString(env->isolate()); - enum encoding encoding = ParseEncoding(env->isolate(), - chunks->Get(i * 2 + 1)); - size_t chunk_size; - if (encoding == UTF8 && string->Length() > 65535) - chunk_size = StringBytes::Size(env->isolate(), string, encoding); - else - chunk_size = StringBytes::StorageSize(env->isolate(), string, encoding); - - storage_size += chunk_size; - } + uint32_t bytes = 0; + size_t offset; + AsyncWrap* wrap; + WriteWrap* req_wrap; + int err; - if (storage_size > INT_MAX) - return UV_ENOBUFS; + if (!all_buffers) { + // Determine storage size first + for (size_t i = 0; i < count; i++) { + storage_size = ROUND_UP(storage_size, WriteWrap::kAlignSize); - AsyncWrap* wrap = GetAsyncWrap(); - CHECK_NE(wrap, nullptr); - env->set_init_trigger_id(wrap->get_id()); - WriteWrap* req_wrap = WriteWrap::New(env, - req_wrap_obj, - this, - AfterWrite, - storage_size); + Local chunk = chunks->Get(i * 2); - uint32_t bytes = 0; - size_t offset = 0; - for (size_t i = 0; i < count; i++) { - Local chunk = chunks->Get(i * 2); + if (Buffer::HasInstance(chunk)) + continue; + // Buffer chunk, no additional storage required + + // String chunk + Local string = chunk->ToString(env->isolate()); + enum encoding encoding = ParseEncoding(env->isolate(), + chunks->Get(i * 2 + 1)); + size_t chunk_size; + if (encoding == UTF8 && string->Length() > 65535) + chunk_size = StringBytes::Size(env->isolate(), string, encoding); + else + chunk_size = StringBytes::StorageSize(env->isolate(), string, encoding); - // Write buffer - if (Buffer::HasInstance(chunk)) { + storage_size += chunk_size; + } + + if (storage_size > INT_MAX) + return UV_ENOBUFS; + } else { + for (size_t i = 0; i < count; i++) { + Local chunk = chunks->Get(i); bufs[i].base = Buffer::Data(chunk); bufs[i].len = Buffer::Length(chunk); bytes += bufs[i].len; - continue; } - // Write string - offset = ROUND_UP(offset, WriteWrap::kAlignSize); - CHECK_LE(offset, storage_size); - char* str_storage = req_wrap->Extra(offset); - size_t str_size = storage_size - offset; - - Local string = chunk->ToString(env->isolate()); - enum encoding encoding = ParseEncoding(env->isolate(), - chunks->Get(i * 2 + 1)); - str_size = StringBytes::Write(env->isolate(), - str_storage, - str_size, - string, - encoding); - bufs[i].base = str_storage; - bufs[i].len = str_size; - offset += str_size; - bytes += str_size; + // Try writing immediately without allocation + err = DoTryWrite(&buf_list, &count); + if (err != 0 || count == 0) + goto done; } - int err = DoWrite(req_wrap, *bufs, count, nullptr); + wrap = GetAsyncWrap(); + CHECK_NE(wrap, nullptr); + env->set_init_trigger_id(wrap->get_id()); + req_wrap = WriteWrap::New(env, req_wrap_obj, this, AfterWrite, storage_size); + offset = 0; + if (!all_buffers) { + for (size_t i = 0; i < count; i++) { + Local chunk = chunks->Get(i * 2); + + // Write buffer + if (Buffer::HasInstance(chunk)) { + bufs[i].base = Buffer::Data(chunk); + bufs[i].len = Buffer::Length(chunk); + bytes += bufs[i].len; + continue; + } + + // Write string + offset = ROUND_UP(offset, WriteWrap::kAlignSize); + CHECK_LE(offset, storage_size); + char* str_storage = req_wrap->Extra(offset); + size_t str_size = storage_size - offset; + + Local string = chunk->ToString(env->isolate()); + enum encoding encoding = ParseEncoding(env->isolate(), + chunks->Get(i * 2 + 1)); + str_size = StringBytes::Write(env->isolate(), + str_storage, + str_size, + string, + encoding); + bufs[i].base = str_storage; + bufs[i].len = str_size; + offset += str_size; + bytes += str_size; + } + } + + err = DoWrite(req_wrap, buf_list, count, nullptr); req_wrap_obj->Set(env->async(), True(env->isolate())); - req_wrap_obj->Set(env->bytes_string(), Number::New(env->isolate(), bytes)); + + if (err) + req_wrap->Dispose(); + + done: const char* msg = Error(); if (msg != nullptr) { req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); ClearError(); } - - if (err) - req_wrap->Dispose(); + req_wrap_obj->Set(env->bytes_string(), Number::New(env->isolate(), bytes)); return err; } -- cgit v1.2.1