Skip to content

Commit 7df018a

Browse files
trevnorrisFishrock123
authored andcommitted
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: #2866 Reviewed-By: Fedor Indutny <[email protected]>
1 parent 431bf74 commit 7df018a

File tree

6 files changed

+107
-61
lines changed

6 files changed

+107
-61
lines changed

lib/buffer.js

+21-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
const binding = process.binding('buffer');
55
const internalUtil = require('internal/util');
6+
const bindingObj = {};
67

78
exports.Buffer = Buffer;
89
exports.SlowBuffer = SlowBuffer;
@@ -14,11 +15,19 @@ Buffer.poolSize = 8 * 1024;
1415
var poolSize, poolOffset, allocPool;
1516

1617

18+
binding.setupBufferJS(Buffer.prototype, bindingObj);
19+
const flags = bindingObj.flags;
20+
const kNoZeroFill = 0;
21+
22+
1723
function createPool() {
1824
poolSize = Buffer.poolSize;
19-
allocPool = binding.create(poolSize);
25+
flags[kNoZeroFill] = 1;
26+
allocPool = new Uint8Array(poolSize);
27+
Object.setPrototypeOf(allocPool, Buffer.prototype);
2028
poolOffset = 0;
2129
}
30+
createPool();
2231

2332

2433
function alignPool() {
@@ -46,30 +55,28 @@ function Buffer(arg) {
4655

4756
// Unusual.
4857
return fromObject(arg);
49-
};
58+
}
5059

5160
Buffer.prototype.__proto__ = Uint8Array.prototype;
5261
Buffer.__proto__ = Uint8Array;
5362

5463

55-
binding.setupBufferJS(Buffer.prototype);
56-
// Buffer prototype must be past before creating our first pool.
57-
createPool();
58-
59-
6064
function SlowBuffer(length) {
6165
if (+length != length)
6266
length = 0;
63-
return binding.create(+length);
64-
};
67+
flags[kNoZeroFill] = 1;
68+
const ui8 = new Uint8Array(+length);
69+
Object.setPrototypeOf(ui8, Buffer.prototype);
70+
return ui8;
71+
}
6572

6673
SlowBuffer.prototype.__proto__ = Buffer.prototype;
6774
SlowBuffer.__proto__ = Buffer;
6875

6976

7077
function allocate(size) {
7178
if (size === 0)
72-
return binding.create(0);
79+
return SlowBuffer(0);
7380
if (size < (Buffer.poolSize >>> 1)) {
7481
if (size > (poolSize - poolOffset))
7582
createPool();
@@ -78,7 +85,10 @@ function allocate(size) {
7885
alignPool();
7986
return b;
8087
} else {
81-
return binding.create(size);
88+
flags[kNoZeroFill] = 1;
89+
const ui8 = new Uint8Array(size);
90+
Object.setPrototypeOf(ui8, Buffer.prototype);
91+
return ui8;
8292
}
8393
}
8494

src/env-inl.h

+26
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,27 @@ inline void Environment::TickInfo::set_last_threw(bool value) {
131131
last_threw_ = value;
132132
}
133133

134+
inline Environment::ArrayBufferAllocatorInfo::ArrayBufferAllocatorInfo() {
135+
for (int i = 0; i < kFieldsCount; ++i)
136+
fields_[i] = 0;
137+
}
138+
139+
inline uint32_t* Environment::ArrayBufferAllocatorInfo::fields() {
140+
return fields_;
141+
}
142+
143+
inline int Environment::ArrayBufferAllocatorInfo::fields_count() const {
144+
return kFieldsCount;
145+
}
146+
147+
inline bool Environment::ArrayBufferAllocatorInfo::no_zero_fill() const {
148+
return fields_[kNoZeroFill] != 0;
149+
}
150+
151+
inline void Environment::ArrayBufferAllocatorInfo::reset_fill_flag() {
152+
fields_[kNoZeroFill] = 0;
153+
}
154+
134155
inline Environment* Environment::New(v8::Local<v8::Context> context,
135156
uv_loop_t* loop) {
136157
Environment* env = new Environment(context, loop);
@@ -290,6 +311,11 @@ inline Environment::TickInfo* Environment::tick_info() {
290311
return &tick_info_;
291312
}
292313

314+
inline Environment::ArrayBufferAllocatorInfo*
315+
Environment::array_buffer_allocator_info() {
316+
return &array_buffer_allocator_info_;
317+
}
318+
293319
inline uint64_t Environment::timer_base() const {
294320
return timer_base_;
295321
}

src/env.h

+23
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,27 @@ class Environment {
339339
DISALLOW_COPY_AND_ASSIGN(TickInfo);
340340
};
341341

342+
class ArrayBufferAllocatorInfo {
343+
public:
344+
inline uint32_t* fields();
345+
inline int fields_count() const;
346+
inline bool no_zero_fill() const;
347+
inline void reset_fill_flag();
348+
349+
private:
350+
friend class Environment; // So we can call the constructor.
351+
inline ArrayBufferAllocatorInfo();
352+
353+
enum Fields {
354+
kNoZeroFill,
355+
kFieldsCount
356+
};
357+
358+
uint32_t fields_[kFieldsCount];
359+
360+
DISALLOW_COPY_AND_ASSIGN(ArrayBufferAllocatorInfo);
361+
};
362+
342363
typedef void (*HandleCleanupCb)(Environment* env,
343364
uv_handle_t* handle,
344365
void* arg);
@@ -401,6 +422,7 @@ class Environment {
401422
inline AsyncHooks* async_hooks();
402423
inline DomainFlag* domain_flag();
403424
inline TickInfo* tick_info();
425+
inline ArrayBufferAllocatorInfo* array_buffer_allocator_info();
404426
inline uint64_t timer_base() const;
405427

406428
static inline Environment* from_cares_timer_handle(uv_timer_t* handle);
@@ -510,6 +532,7 @@ class Environment {
510532
AsyncHooks async_hooks_;
511533
DomainFlag domain_flag_;
512534
TickInfo tick_info_;
535+
ArrayBufferAllocatorInfo array_buffer_allocator_info_;
513536
const uint64_t timer_base_;
514537
uv_timer_t cares_timer_handle_;
515538
ares_channel cares_channel_;

src/node.cc

+13-2
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,14 @@ Local<Value> WinapiErrnoException(Isolate* isolate,
894894
#endif
895895

896896

897+
void* ArrayBufferAllocator::Allocate(size_t size) {
898+
if (env_ == nullptr || !env_->array_buffer_allocator_info()->no_zero_fill())
899+
return calloc(size, 1);
900+
env_->array_buffer_allocator_info()->reset_fill_flag();
901+
return malloc(size);
902+
}
903+
904+
897905
void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
898906
Environment* env = Environment::GetCurrent(args);
899907

@@ -3879,8 +3887,8 @@ Environment* CreateEnvironment(Isolate* isolate,
38793887
static void StartNodeInstance(void* arg) {
38803888
NodeInstanceData* instance_data = static_cast<NodeInstanceData*>(arg);
38813889
Isolate::CreateParams params;
3882-
ArrayBufferAllocator array_buffer_allocator;
3883-
params.array_buffer_allocator = &array_buffer_allocator;
3890+
ArrayBufferAllocator* array_buffer_allocator = new ArrayBufferAllocator();
3891+
params.array_buffer_allocator = array_buffer_allocator;
38843892
Isolate* isolate = Isolate::New(params);
38853893
if (track_heap_objects) {
38863894
isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
@@ -3896,6 +3904,7 @@ static void StartNodeInstance(void* arg) {
38963904
HandleScope handle_scope(isolate);
38973905
Local<Context> context = Context::New(isolate);
38983906
Environment* env = CreateEnvironment(isolate, context, instance_data);
3907+
array_buffer_allocator->set_env(env);
38993908
Context::Scope context_scope(context);
39003909
if (instance_data->is_main())
39013910
env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception);
@@ -3942,13 +3951,15 @@ static void StartNodeInstance(void* arg) {
39423951
__lsan_do_leak_check();
39433952
#endif
39443953

3954+
array_buffer_allocator->set_env(nullptr);
39453955
env->Dispose();
39463956
env = nullptr;
39473957
}
39483958

39493959
CHECK_NE(isolate, nullptr);
39503960
isolate->Dispose();
39513961
isolate = nullptr;
3962+
delete array_buffer_allocator;
39523963
if (instance_data->is_main())
39533964
node_isolate = nullptr;
39543965
}

src/node_buffer.cc

+14-38
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ using v8::Object;
6868
using v8::Persistent;
6969
using v8::String;
7070
using v8::Uint32;
71+
using v8::Uint32Array;
7172
using v8::Uint8Array;
7273
using v8::Value;
7374
using v8::WeakCallbackData;
@@ -392,43 +393,6 @@ MaybeLocal<Object> New(Environment* env, char* data, size_t length) {
392393
}
393394

394395

395-
void Create(const FunctionCallbackInfo<Value>& args) {
396-
Isolate* isolate = args.GetIsolate();
397-
Environment* env = Environment::GetCurrent(args);
398-
399-
CHECK(args[0]->IsNumber());
400-
401-
int64_t length = args[0]->IntegerValue();
402-
403-
if (length < 0 || length > kMaxLength) {
404-
return env->ThrowRangeError("invalid Buffer length");
405-
}
406-
407-
void* data;
408-
if (length > 0) {
409-
data = malloc(length);
410-
if (data == nullptr) {
411-
return env->ThrowRangeError(
412-
"Buffer allocation failed - process out of memory");
413-
}
414-
} else {
415-
data = nullptr;
416-
}
417-
418-
Local<ArrayBuffer> ab =
419-
ArrayBuffer::New(isolate,
420-
data,
421-
length,
422-
ArrayBufferCreationMode::kInternalized);
423-
Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
424-
Maybe<bool> mb =
425-
ui->SetPrototype(env->context(), env->buffer_prototype_object());
426-
if (!mb.FromMaybe(false))
427-
return env->ThrowError("Unable to set Object prototype");
428-
args.GetReturnValue().Set(ui);
429-
}
430-
431-
432396
void CreateFromString(const FunctionCallbackInfo<Value>& args) {
433397
CHECK(args[0]->IsString());
434398
CHECK(args[1]->IsString());
@@ -966,6 +930,19 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
966930
env->SetMethod(proto, "utf8Write", Utf8Write);
967931

968932
env->SetMethod(proto, "copy", Copy);
933+
934+
CHECK(args[1]->IsObject());
935+
Local<Object> bObj = args[1].As<Object>();
936+
937+
uint32_t* const fields = env->array_buffer_allocator_info()->fields();
938+
uint32_t const fields_count =
939+
env->array_buffer_allocator_info()->fields_count();
940+
941+
Local<ArrayBuffer> array_buffer =
942+
ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
943+
944+
bObj->Set(String::NewFromUtf8(env->isolate(), "flags"),
945+
Uint32Array::New(array_buffer, 0, fields_count));
969946
}
970947

971948

@@ -975,7 +952,6 @@ void Initialize(Local<Object> target,
975952
Environment* env = Environment::GetCurrent(context);
976953

977954
env->SetMethod(target, "setupBufferJS", SetupBufferJS);
978-
env->SetMethod(target, "create", Create);
979955
env->SetMethod(target, "createFromString", CreateFromString);
980956
env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer);
981957

src/node_internals.h

+10-10
Original file line numberDiff line numberDiff line change
@@ -228,18 +228,18 @@ NODE_DEPRECATED("Use ThrowUVException(isolate)",
228228
return ThrowUVException(isolate, errorno, syscall, message, path);
229229
})
230230

231-
struct ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
232-
virtual void* Allocate(size_t size) {
233-
return calloc(size, 1);
234-
}
231+
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
232+
public:
233+
ArrayBufferAllocator() { }
235234

236-
virtual void* AllocateUninitialized(size_t size) {
237-
return malloc(size);
238-
}
235+
inline void set_env(Environment* env) { env_ = env; }
239236

240-
virtual void Free(void* data, size_t) {
241-
free(data);
242-
}
237+
virtual void* Allocate(size_t size); // Defined in src/node.cc
238+
virtual void* AllocateUninitialized(size_t size) { return malloc(size); }
239+
virtual void Free(void* data, size_t) { free(data); }
240+
241+
private:
242+
Environment* env_;
243243
};
244244

245245
enum NodeInstanceType { MAIN, WORKER };

0 commit comments

Comments
 (0)