From 7df018a29bf7619d8d979d0db821d97f16e5550f Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Mon, 14 Sep 2015 16:31:10 -0600 Subject: [PATCH] buffer: construct Uint8Array in JS Overall construction time of Typed Arrays is faster in JS, but the problem with using it normally is zero-fill of memory. Get around this by using a flag in the ArrayBuffer::Allocator to trigger when memory should or shouldn't be zero-filled. Remove Buffer::Create() as it is no longer called. The creation of the Uint8Array() was done at each callsite because at the time of this patch there was a performance penalty for centralizing the call in a single function. PR-URL: https://github.com/nodejs/node/pull/2866 Reviewed-By: Fedor Indutny --- lib/buffer.js | 32 +++++++++++++++++++++----------- src/env-inl.h | 26 ++++++++++++++++++++++++++ src/env.h | 23 +++++++++++++++++++++++ src/node.cc | 15 +++++++++++++-- src/node_buffer.cc | 52 ++++++++++++++-------------------------------------- src/node_internals.h | 20 ++++++++++---------- 6 files changed, 107 insertions(+), 61 deletions(-) diff --git a/lib/buffer.js b/lib/buffer.js index 37ed0f9..26b51a8 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -3,6 +3,7 @@ const binding = process.binding('buffer'); const internalUtil = require('internal/util'); +const bindingObj = {}; exports.Buffer = Buffer; exports.SlowBuffer = SlowBuffer; @@ -14,11 +15,19 @@ Buffer.poolSize = 8 * 1024; var poolSize, poolOffset, allocPool; +binding.setupBufferJS(Buffer.prototype, bindingObj); +const flags = bindingObj.flags; +const kNoZeroFill = 0; + + function createPool() { poolSize = Buffer.poolSize; - allocPool = binding.create(poolSize); + flags[kNoZeroFill] = 1; + allocPool = new Uint8Array(poolSize); + Object.setPrototypeOf(allocPool, Buffer.prototype); poolOffset = 0; } +createPool(); function alignPool() { @@ -46,22 +55,20 @@ function Buffer(arg) { // Unusual. return fromObject(arg); -}; +} Buffer.prototype.__proto__ = Uint8Array.prototype; Buffer.__proto__ = Uint8Array; -binding.setupBufferJS(Buffer.prototype); -// Buffer prototype must be past before creating our first pool. -createPool(); - - function SlowBuffer(length) { if (+length != length) length = 0; - return binding.create(+length); -}; + flags[kNoZeroFill] = 1; + const ui8 = new Uint8Array(+length); + Object.setPrototypeOf(ui8, Buffer.prototype); + return ui8; +} SlowBuffer.prototype.__proto__ = Buffer.prototype; SlowBuffer.__proto__ = Buffer; @@ -69,7 +76,7 @@ SlowBuffer.__proto__ = Buffer; function allocate(size) { if (size === 0) - return binding.create(0); + return SlowBuffer(0); if (size < (Buffer.poolSize >>> 1)) { if (size > (poolSize - poolOffset)) createPool(); @@ -78,7 +85,10 @@ function allocate(size) { alignPool(); return b; } else { - return binding.create(size); + flags[kNoZeroFill] = 1; + const ui8 = new Uint8Array(size); + Object.setPrototypeOf(ui8, Buffer.prototype); + return ui8; } } diff --git a/src/env-inl.h b/src/env-inl.h index cbc8c4f..cc9dd9e 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -131,6 +131,27 @@ inline void Environment::TickInfo::set_last_threw(bool value) { last_threw_ = value; } +inline Environment::ArrayBufferAllocatorInfo::ArrayBufferAllocatorInfo() { + for (int i = 0; i < kFieldsCount; ++i) + fields_[i] = 0; +} + +inline uint32_t* Environment::ArrayBufferAllocatorInfo::fields() { + return fields_; +} + +inline int Environment::ArrayBufferAllocatorInfo::fields_count() const { + return kFieldsCount; +} + +inline bool Environment::ArrayBufferAllocatorInfo::no_zero_fill() const { + return fields_[kNoZeroFill] != 0; +} + +inline void Environment::ArrayBufferAllocatorInfo::reset_fill_flag() { + fields_[kNoZeroFill] = 0; +} + inline Environment* Environment::New(v8::Local context, uv_loop_t* loop) { Environment* env = new Environment(context, loop); @@ -290,6 +311,11 @@ inline Environment::TickInfo* Environment::tick_info() { return &tick_info_; } +inline Environment::ArrayBufferAllocatorInfo* + Environment::array_buffer_allocator_info() { + return &array_buffer_allocator_info_; +} + inline uint64_t Environment::timer_base() const { return timer_base_; } diff --git a/src/env.h b/src/env.h index ce972d5..1107dcb 100644 --- a/src/env.h +++ b/src/env.h @@ -339,6 +339,27 @@ class Environment { DISALLOW_COPY_AND_ASSIGN(TickInfo); }; + class ArrayBufferAllocatorInfo { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline bool no_zero_fill() const; + inline void reset_fill_flag(); + + private: + friend class Environment; // So we can call the constructor. + inline ArrayBufferAllocatorInfo(); + + enum Fields { + kNoZeroFill, + kFieldsCount + }; + + uint32_t fields_[kFieldsCount]; + + DISALLOW_COPY_AND_ASSIGN(ArrayBufferAllocatorInfo); + }; + typedef void (*HandleCleanupCb)(Environment* env, uv_handle_t* handle, void* arg); @@ -401,6 +422,7 @@ class Environment { inline AsyncHooks* async_hooks(); inline DomainFlag* domain_flag(); inline TickInfo* tick_info(); + inline ArrayBufferAllocatorInfo* array_buffer_allocator_info(); inline uint64_t timer_base() const; static inline Environment* from_cares_timer_handle(uv_timer_t* handle); @@ -510,6 +532,7 @@ class Environment { AsyncHooks async_hooks_; DomainFlag domain_flag_; TickInfo tick_info_; + ArrayBufferAllocatorInfo array_buffer_allocator_info_; const uint64_t timer_base_; uv_timer_t cares_timer_handle_; ares_channel cares_channel_; diff --git a/src/node.cc b/src/node.cc index a6f66d4..b0ba02a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -894,6 +894,14 @@ Local WinapiErrnoException(Isolate* isolate, #endif +void* ArrayBufferAllocator::Allocate(size_t size) { + if (env_ == nullptr || !env_->array_buffer_allocator_info()->no_zero_fill()) + return calloc(size, 1); + env_->array_buffer_allocator_info()->reset_fill_flag(); + return malloc(size); +} + + void SetupDomainUse(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -3879,8 +3887,8 @@ Environment* CreateEnvironment(Isolate* isolate, static void StartNodeInstance(void* arg) { NodeInstanceData* instance_data = static_cast(arg); Isolate::CreateParams params; - ArrayBufferAllocator array_buffer_allocator; - params.array_buffer_allocator = &array_buffer_allocator; + ArrayBufferAllocator* array_buffer_allocator = new ArrayBufferAllocator(); + params.array_buffer_allocator = array_buffer_allocator; Isolate* isolate = Isolate::New(params); if (track_heap_objects) { isolate->GetHeapProfiler()->StartTrackingHeapObjects(true); @@ -3896,6 +3904,7 @@ static void StartNodeInstance(void* arg) { HandleScope handle_scope(isolate); Local context = Context::New(isolate); Environment* env = CreateEnvironment(isolate, context, instance_data); + array_buffer_allocator->set_env(env); Context::Scope context_scope(context); if (instance_data->is_main()) env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception); @@ -3942,6 +3951,7 @@ static void StartNodeInstance(void* arg) { __lsan_do_leak_check(); #endif + array_buffer_allocator->set_env(nullptr); env->Dispose(); env = nullptr; } @@ -3949,6 +3959,7 @@ static void StartNodeInstance(void* arg) { CHECK_NE(isolate, nullptr); isolate->Dispose(); isolate = nullptr; + delete array_buffer_allocator; if (instance_data->is_main()) node_isolate = nullptr; } diff --git a/src/node_buffer.cc b/src/node_buffer.cc index dc0c742..5e0de52 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -68,6 +68,7 @@ using v8::Object; using v8::Persistent; using v8::String; using v8::Uint32; +using v8::Uint32Array; using v8::Uint8Array; using v8::Value; using v8::WeakCallbackData; @@ -392,43 +393,6 @@ MaybeLocal New(Environment* env, char* data, size_t length) { } -void Create(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsNumber()); - - int64_t length = args[0]->IntegerValue(); - - if (length < 0 || length > kMaxLength) { - return env->ThrowRangeError("invalid Buffer length"); - } - - void* data; - if (length > 0) { - data = malloc(length); - if (data == nullptr) { - return env->ThrowRangeError( - "Buffer allocation failed - process out of memory"); - } - } else { - data = nullptr; - } - - Local ab = - ArrayBuffer::New(isolate, - data, - length, - ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (!mb.FromMaybe(false)) - return env->ThrowError("Unable to set Object prototype"); - args.GetReturnValue().Set(ui); -} - - void CreateFromString(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); CHECK(args[1]->IsString()); @@ -966,6 +930,19 @@ void SetupBufferJS(const FunctionCallbackInfo& args) { env->SetMethod(proto, "utf8Write", Utf8Write); env->SetMethod(proto, "copy", Copy); + + CHECK(args[1]->IsObject()); + Local bObj = args[1].As(); + + uint32_t* const fields = env->array_buffer_allocator_info()->fields(); + uint32_t const fields_count = + env->array_buffer_allocator_info()->fields_count(); + + Local array_buffer = + ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); + + bObj->Set(String::NewFromUtf8(env->isolate(), "flags"), + Uint32Array::New(array_buffer, 0, fields_count)); } @@ -975,7 +952,6 @@ void Initialize(Local target, Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "setupBufferJS", SetupBufferJS); - env->SetMethod(target, "create", Create); env->SetMethod(target, "createFromString", CreateFromString); env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer); diff --git a/src/node_internals.h b/src/node_internals.h index f4d8c1b..5cc439c 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -228,18 +228,18 @@ NODE_DEPRECATED("Use ThrowUVException(isolate)", return ThrowUVException(isolate, errorno, syscall, message, path); }) -struct ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { - virtual void* Allocate(size_t size) { - return calloc(size, 1); - } +class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + public: + ArrayBufferAllocator() { } - virtual void* AllocateUninitialized(size_t size) { - return malloc(size); - } + inline void set_env(Environment* env) { env_ = env; } - virtual void Free(void* data, size_t) { - free(data); - } + virtual void* Allocate(size_t size); // Defined in src/node.cc + virtual void* AllocateUninitialized(size_t size) { return malloc(size); } + virtual void Free(void* data, size_t) { free(data); } + + private: + Environment* env_; }; enum NodeInstanceType { MAIN, WORKER }; -- 2.7.4