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 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<v8::Context> 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_;
|
||||
}
|
||||
|
23
src/env.h
23
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_;
|
||||
|
15
src/node.cc
15
src/node.cc
@ -894,6 +894,14 @@ Local<Value> 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<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
@ -3879,8 +3887,8 @@ Environment* CreateEnvironment(Isolate* isolate,
|
||||
static void StartNodeInstance(void* arg) {
|
||||
NodeInstanceData* instance_data = static_cast<NodeInstanceData*>(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 = 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;
|
||||
}
|
||||
|
@ -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<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) {
|
||||
CHECK(args[0]->IsString());
|
||||
CHECK(args[1]->IsString());
|
||||
@ -966,6 +930,19 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
|
||||
env->SetMethod(proto, "utf8Write", Utf8Write);
|
||||
|
||||
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);
|
||||
|
||||
env->SetMethod(target, "setupBufferJS", SetupBufferJS);
|
||||
env->SetMethod(target, "create", Create);
|
||||
env->SetMethod(target, "createFromString", CreateFromString);
|
||||
env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer);
|
||||
|
||||
|
@ -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 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user