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 <fedor@indutny.com>
This commit is contained in:
parent
7d7941235a
commit
74178a5682
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
const binding = process.binding('buffer');
|
const binding = process.binding('buffer');
|
||||||
const internalUtil = require('internal/util');
|
const internalUtil = require('internal/util');
|
||||||
|
const bindingObj = {};
|
||||||
|
|
||||||
exports.Buffer = Buffer;
|
exports.Buffer = Buffer;
|
||||||
exports.SlowBuffer = SlowBuffer;
|
exports.SlowBuffer = SlowBuffer;
|
||||||
@ -14,11 +15,19 @@ Buffer.poolSize = 8 * 1024;
|
|||||||
var poolSize, poolOffset, allocPool;
|
var poolSize, poolOffset, allocPool;
|
||||||
|
|
||||||
|
|
||||||
|
binding.setupBufferJS(Buffer.prototype, bindingObj);
|
||||||
|
const flags = bindingObj.flags;
|
||||||
|
const kNoZeroFill = 0;
|
||||||
|
|
||||||
|
|
||||||
function createPool() {
|
function createPool() {
|
||||||
poolSize = Buffer.poolSize;
|
poolSize = Buffer.poolSize;
|
||||||
allocPool = binding.create(poolSize);
|
flags[kNoZeroFill] = 1;
|
||||||
|
allocPool = new Uint8Array(poolSize);
|
||||||
|
Object.setPrototypeOf(allocPool, Buffer.prototype);
|
||||||
poolOffset = 0;
|
poolOffset = 0;
|
||||||
}
|
}
|
||||||
|
createPool();
|
||||||
|
|
||||||
|
|
||||||
function alignPool() {
|
function alignPool() {
|
||||||
@ -46,22 +55,20 @@ function Buffer(arg) {
|
|||||||
|
|
||||||
// Unusual.
|
// Unusual.
|
||||||
return fromObject(arg);
|
return fromObject(arg);
|
||||||
};
|
}
|
||||||
|
|
||||||
Buffer.prototype.__proto__ = Uint8Array.prototype;
|
Buffer.prototype.__proto__ = Uint8Array.prototype;
|
||||||
Buffer.__proto__ = Uint8Array;
|
Buffer.__proto__ = Uint8Array;
|
||||||
|
|
||||||
|
|
||||||
binding.setupBufferJS(Buffer.prototype);
|
|
||||||
// Buffer prototype must be past before creating our first pool.
|
|
||||||
createPool();
|
|
||||||
|
|
||||||
|
|
||||||
function SlowBuffer(length) {
|
function SlowBuffer(length) {
|
||||||
if (+length != length)
|
if (+length != length)
|
||||||
length = 0;
|
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.prototype.__proto__ = Buffer.prototype;
|
||||||
SlowBuffer.__proto__ = Buffer;
|
SlowBuffer.__proto__ = Buffer;
|
||||||
@ -69,7 +76,7 @@ SlowBuffer.__proto__ = Buffer;
|
|||||||
|
|
||||||
function allocate(size) {
|
function allocate(size) {
|
||||||
if (size === 0)
|
if (size === 0)
|
||||||
return binding.create(0);
|
return SlowBuffer(0);
|
||||||
if (size < (Buffer.poolSize >>> 1)) {
|
if (size < (Buffer.poolSize >>> 1)) {
|
||||||
if (size > (poolSize - poolOffset))
|
if (size > (poolSize - poolOffset))
|
||||||
createPool();
|
createPool();
|
||||||
@ -78,7 +85,10 @@ function allocate(size) {
|
|||||||
alignPool();
|
alignPool();
|
||||||
return b;
|
return b;
|
||||||
} else {
|
} else {
|
||||||
return binding.create(size);
|
flags[kNoZeroFill] = 1;
|
||||||
|
const ui8 = new Uint8Array(size);
|
||||||
|
Object.setPrototypeOf(ui8, Buffer.prototype);
|
||||||
|
return ui8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +131,27 @@ inline void Environment::TickInfo::set_last_threw(bool value) {
|
|||||||
last_threw_ = 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<v8::Context> context,
|
inline Environment* Environment::New(v8::Local<v8::Context> context,
|
||||||
uv_loop_t* loop) {
|
uv_loop_t* loop) {
|
||||||
Environment* env = new Environment(context, loop);
|
Environment* env = new Environment(context, loop);
|
||||||
@ -290,6 +311,11 @@ inline Environment::TickInfo* Environment::tick_info() {
|
|||||||
return &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 {
|
inline uint64_t Environment::timer_base() const {
|
||||||
return timer_base_;
|
return timer_base_;
|
||||||
}
|
}
|
||||||
|
23
src/env.h
23
src/env.h
@ -339,6 +339,27 @@ class Environment {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(TickInfo);
|
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,
|
typedef void (*HandleCleanupCb)(Environment* env,
|
||||||
uv_handle_t* handle,
|
uv_handle_t* handle,
|
||||||
void* arg);
|
void* arg);
|
||||||
@ -401,6 +422,7 @@ class Environment {
|
|||||||
inline AsyncHooks* async_hooks();
|
inline AsyncHooks* async_hooks();
|
||||||
inline DomainFlag* domain_flag();
|
inline DomainFlag* domain_flag();
|
||||||
inline TickInfo* tick_info();
|
inline TickInfo* tick_info();
|
||||||
|
inline ArrayBufferAllocatorInfo* array_buffer_allocator_info();
|
||||||
inline uint64_t timer_base() const;
|
inline uint64_t timer_base() const;
|
||||||
|
|
||||||
static inline Environment* from_cares_timer_handle(uv_timer_t* handle);
|
static inline Environment* from_cares_timer_handle(uv_timer_t* handle);
|
||||||
@ -510,6 +532,7 @@ class Environment {
|
|||||||
AsyncHooks async_hooks_;
|
AsyncHooks async_hooks_;
|
||||||
DomainFlag domain_flag_;
|
DomainFlag domain_flag_;
|
||||||
TickInfo tick_info_;
|
TickInfo tick_info_;
|
||||||
|
ArrayBufferAllocatorInfo array_buffer_allocator_info_;
|
||||||
const uint64_t timer_base_;
|
const uint64_t timer_base_;
|
||||||
uv_timer_t cares_timer_handle_;
|
uv_timer_t cares_timer_handle_;
|
||||||
ares_channel cares_channel_;
|
ares_channel cares_channel_;
|
||||||
|
15
src/node.cc
15
src/node.cc
@ -894,6 +894,14 @@ Local<Value> WinapiErrnoException(Isolate* isolate,
|
|||||||
#endif
|
#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<Value>& args) {
|
void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
@ -3879,8 +3887,8 @@ Environment* CreateEnvironment(Isolate* isolate,
|
|||||||
static void StartNodeInstance(void* arg) {
|
static void StartNodeInstance(void* arg) {
|
||||||
NodeInstanceData* instance_data = static_cast<NodeInstanceData*>(arg);
|
NodeInstanceData* instance_data = static_cast<NodeInstanceData*>(arg);
|
||||||
Isolate::CreateParams params;
|
Isolate::CreateParams params;
|
||||||
ArrayBufferAllocator array_buffer_allocator;
|
ArrayBufferAllocator* array_buffer_allocator = new ArrayBufferAllocator();
|
||||||
params.array_buffer_allocator = &array_buffer_allocator;
|
params.array_buffer_allocator = array_buffer_allocator;
|
||||||
Isolate* isolate = Isolate::New(params);
|
Isolate* isolate = Isolate::New(params);
|
||||||
if (track_heap_objects) {
|
if (track_heap_objects) {
|
||||||
isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
|
isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
|
||||||
@ -3896,6 +3904,7 @@ static void StartNodeInstance(void* arg) {
|
|||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
Local<Context> context = Context::New(isolate);
|
Local<Context> context = Context::New(isolate);
|
||||||
Environment* env = CreateEnvironment(isolate, context, instance_data);
|
Environment* env = CreateEnvironment(isolate, context, instance_data);
|
||||||
|
array_buffer_allocator->set_env(env);
|
||||||
Context::Scope context_scope(context);
|
Context::Scope context_scope(context);
|
||||||
if (instance_data->is_main())
|
if (instance_data->is_main())
|
||||||
env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception);
|
env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception);
|
||||||
@ -3942,6 +3951,7 @@ static void StartNodeInstance(void* arg) {
|
|||||||
__lsan_do_leak_check();
|
__lsan_do_leak_check();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
array_buffer_allocator->set_env(nullptr);
|
||||||
env->Dispose();
|
env->Dispose();
|
||||||
env = nullptr;
|
env = nullptr;
|
||||||
}
|
}
|
||||||
@ -3949,6 +3959,7 @@ static void StartNodeInstance(void* arg) {
|
|||||||
CHECK_NE(isolate, nullptr);
|
CHECK_NE(isolate, nullptr);
|
||||||
isolate->Dispose();
|
isolate->Dispose();
|
||||||
isolate = nullptr;
|
isolate = nullptr;
|
||||||
|
delete array_buffer_allocator;
|
||||||
if (instance_data->is_main())
|
if (instance_data->is_main())
|
||||||
node_isolate = nullptr;
|
node_isolate = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ using v8::Object;
|
|||||||
using v8::Persistent;
|
using v8::Persistent;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Uint32;
|
using v8::Uint32;
|
||||||
|
using v8::Uint32Array;
|
||||||
using v8::Uint8Array;
|
using v8::Uint8Array;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
using v8::WeakCallbackData;
|
using v8::WeakCallbackData;
|
||||||
@ -392,43 +393,6 @@ MaybeLocal<Object> New(Environment* env, char* data, size_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Create(const FunctionCallbackInfo<Value>& 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<ArrayBuffer> ab =
|
|
||||||
ArrayBuffer::New(isolate,
|
|
||||||
data,
|
|
||||||
length,
|
|
||||||
ArrayBufferCreationMode::kInternalized);
|
|
||||||
Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
|
|
||||||
Maybe<bool> 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<Value>& args) {
|
void CreateFromString(const FunctionCallbackInfo<Value>& args) {
|
||||||
CHECK(args[0]->IsString());
|
CHECK(args[0]->IsString());
|
||||||
CHECK(args[1]->IsString());
|
CHECK(args[1]->IsString());
|
||||||
@ -966,6 +930,19 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
|
|||||||
env->SetMethod(proto, "utf8Write", Utf8Write);
|
env->SetMethod(proto, "utf8Write", Utf8Write);
|
||||||
|
|
||||||
env->SetMethod(proto, "copy", Copy);
|
env->SetMethod(proto, "copy", Copy);
|
||||||
|
|
||||||
|
CHECK(args[1]->IsObject());
|
||||||
|
Local<Object> bObj = args[1].As<Object>();
|
||||||
|
|
||||||
|
uint32_t* const fields = env->array_buffer_allocator_info()->fields();
|
||||||
|
uint32_t const fields_count =
|
||||||
|
env->array_buffer_allocator_info()->fields_count();
|
||||||
|
|
||||||
|
Local<ArrayBuffer> 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<Object> target,
|
|||||||
Environment* env = Environment::GetCurrent(context);
|
Environment* env = Environment::GetCurrent(context);
|
||||||
|
|
||||||
env->SetMethod(target, "setupBufferJS", SetupBufferJS);
|
env->SetMethod(target, "setupBufferJS", SetupBufferJS);
|
||||||
env->SetMethod(target, "create", Create);
|
|
||||||
env->SetMethod(target, "createFromString", CreateFromString);
|
env->SetMethod(target, "createFromString", CreateFromString);
|
||||||
env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer);
|
env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer);
|
||||||
|
|
||||||
|
@ -228,18 +228,18 @@ NODE_DEPRECATED("Use ThrowUVException(isolate)",
|
|||||||
return ThrowUVException(isolate, errorno, syscall, message, path);
|
return ThrowUVException(isolate, errorno, syscall, message, path);
|
||||||
})
|
})
|
||||||
|
|
||||||
struct ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
||||||
virtual void* Allocate(size_t size) {
|
public:
|
||||||
return calloc(size, 1);
|
ArrayBufferAllocator() { }
|
||||||
}
|
|
||||||
|
|
||||||
virtual void* AllocateUninitialized(size_t size) {
|
inline void set_env(Environment* env) { env_ = env; }
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Free(void* data, size_t) {
|
virtual void* Allocate(size_t size); // Defined in src/node.cc
|
||||||
free(data);
|
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 };
|
enum NodeInstanceType { MAIN, WORKER };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user