Browse Source

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>
v5.x
Trevor Norris 9 years ago
parent
commit
74178a5682
  1. 32
      lib/buffer.js
  2. 26
      src/env-inl.h
  3. 23
      src/env.h
  4. 15
      src/node.cc
  5. 52
      src/node_buffer.cc
  6. 20
      src/node_internals.h

32
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;
}
}

26
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<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

@ -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

@ -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;
}

52
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<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);

20
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 };

Loading…
Cancel
Save