slab_allocator: remove SlabAllocator
Now that Buffer instantiation has improved, the SlabAllocator is an unnecessary layer of complexity preventing further performance optimizations. Currently there is a small performance loss with very small stream requests, but this will soon be addressed.
This commit is contained in:
parent
c1db1ecd15
commit
ec90e6e80a
2
node.gyp
2
node.gyp
@ -108,7 +108,6 @@
|
||||
'src/smalloc.cc',
|
||||
'src/string_bytes.cc',
|
||||
'src/stream_wrap.cc',
|
||||
'src/slab_allocator.cc',
|
||||
'src/tcp_wrap.cc',
|
||||
'src/timer_wrap.cc',
|
||||
'src/tty_wrap.cc',
|
||||
@ -140,7 +139,6 @@
|
||||
'src/tcp_wrap.h',
|
||||
'src/udp_wrap.h',
|
||||
'src/req_wrap.h',
|
||||
'src/slab_allocator.h',
|
||||
'src/string_bytes.h',
|
||||
'src/stream_wrap.h',
|
||||
'src/tree.h',
|
||||
|
@ -1,127 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "v8.h"
|
||||
#include "node.h"
|
||||
#include "node_buffer.h"
|
||||
#include "slab_allocator.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
using v8::Handle;
|
||||
using v8::HandleScope;
|
||||
using v8::Integer;
|
||||
using v8::Local;
|
||||
using v8::Null;
|
||||
using v8::Object;
|
||||
using v8::Persistent;
|
||||
using v8::String;
|
||||
using v8::V8;
|
||||
using v8::Value;
|
||||
|
||||
|
||||
namespace node {
|
||||
|
||||
SlabAllocator::SlabAllocator(unsigned int size) {
|
||||
size_ = ROUND_UP(size ? size : 1, 8192);
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
|
||||
SlabAllocator::~SlabAllocator() {
|
||||
if (!initialized_) return;
|
||||
if (V8::IsDead()) return;
|
||||
slab_sym_.Dispose(node_isolate);
|
||||
slab_sym_.Clear();
|
||||
slab_.Dispose(node_isolate);
|
||||
slab_.Clear();
|
||||
}
|
||||
|
||||
|
||||
void SlabAllocator::Initialize() {
|
||||
HandleScope scope(node_isolate);
|
||||
char sym[256];
|
||||
snprintf(sym, sizeof(sym), "slab_%p", this); // namespace object key
|
||||
offset_ = 0;
|
||||
last_ptr_ = NULL;
|
||||
initialized_ = true;
|
||||
slab_sym_ = Persistent<String>::New(node_isolate, String::New(sym));
|
||||
}
|
||||
|
||||
|
||||
static Local<Object> NewSlab(unsigned int size) {
|
||||
HandleScope scope(node_isolate);
|
||||
Local<Object> buf = Buffer::New(ROUND_UP(size, 16));
|
||||
return scope.Close(buf);
|
||||
}
|
||||
|
||||
|
||||
char* SlabAllocator::Allocate(Handle<Object> obj, unsigned int size) {
|
||||
HandleScope scope(node_isolate);
|
||||
|
||||
assert(!obj.IsEmpty());
|
||||
|
||||
if (size == 0) return NULL;
|
||||
if (!initialized_) Initialize();
|
||||
|
||||
if (size > size_) {
|
||||
Local<Object> buf = NewSlab(size);
|
||||
obj->SetHiddenValue(slab_sym_, buf);
|
||||
return Buffer::Data(buf);
|
||||
}
|
||||
|
||||
if (slab_.IsEmpty() || offset_ + size > size_) {
|
||||
slab_.Dispose(node_isolate);
|
||||
slab_.Clear();
|
||||
slab_ = Persistent<Object>::New(node_isolate, NewSlab(size_));
|
||||
offset_ = 0;
|
||||
last_ptr_ = NULL;
|
||||
}
|
||||
|
||||
obj->SetHiddenValue(slab_sym_, slab_);
|
||||
last_ptr_ = Buffer::Data(slab_) + offset_;
|
||||
offset_ += size;
|
||||
|
||||
return last_ptr_;
|
||||
}
|
||||
|
||||
|
||||
Local<Object> SlabAllocator::Shrink(Handle<Object> obj,
|
||||
char* ptr,
|
||||
unsigned int size) {
|
||||
HandleScope scope(node_isolate);
|
||||
Local<Value> slab_v = obj->GetHiddenValue(slab_sym_);
|
||||
obj->SetHiddenValue(slab_sym_, Null(node_isolate));
|
||||
assert(!slab_v.IsEmpty());
|
||||
assert(slab_v->IsObject());
|
||||
Local<Object> slab = slab_v->ToObject();
|
||||
assert(ptr != NULL);
|
||||
if (ptr == last_ptr_) {
|
||||
last_ptr_ = NULL;
|
||||
offset_ = ptr - Buffer::Data(slab) + ROUND_UP(size, 16);
|
||||
}
|
||||
return scope.Close(slab);
|
||||
}
|
||||
|
||||
|
||||
} // namespace node
|
@ -1,49 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
class SlabAllocator {
|
||||
public:
|
||||
SlabAllocator(unsigned int size = 10485760); // default to 10M
|
||||
~SlabAllocator();
|
||||
|
||||
// allocate memory from slab, attaches the slice to `obj`
|
||||
char* Allocate(v8::Handle<v8::Object> obj, unsigned int size);
|
||||
|
||||
// return excess memory to the slab, returns a handle to the parent buffer
|
||||
v8::Local<v8::Object> Shrink(v8::Handle<v8::Object> obj,
|
||||
char* ptr,
|
||||
unsigned int size);
|
||||
|
||||
private:
|
||||
void Initialize();
|
||||
bool initialized_;
|
||||
v8::Persistent<v8::Object> slab_;
|
||||
v8::Persistent<v8::String> slab_sym_;
|
||||
unsigned int offset_;
|
||||
unsigned int size_;
|
||||
char* last_ptr_;
|
||||
};
|
||||
|
||||
} // namespace node
|
@ -22,7 +22,6 @@
|
||||
#include "node.h"
|
||||
#include "node_buffer.h"
|
||||
#include "handle_wrap.h"
|
||||
#include "slab_allocator.h"
|
||||
#include "stream_wrap.h"
|
||||
#include "pipe_wrap.h"
|
||||
#include "tcp_wrap.h"
|
||||
@ -33,8 +32,6 @@
|
||||
#include <stdlib.h> // abort()
|
||||
#include <limits.h> // INT_MAX
|
||||
|
||||
#define SLAB_SIZE (1024 * 1024)
|
||||
|
||||
|
||||
namespace node {
|
||||
|
||||
@ -49,6 +46,7 @@ using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::Persistent;
|
||||
using v8::String;
|
||||
using v8::Uint32;
|
||||
using v8::Value;
|
||||
|
||||
|
||||
@ -58,23 +56,13 @@ static Persistent<String> write_queue_size_sym;
|
||||
static Persistent<String> onread_sym;
|
||||
static Persistent<String> oncomplete_sym;
|
||||
static Persistent<String> handle_sym;
|
||||
static SlabAllocator* slab_allocator;
|
||||
static bool initialized;
|
||||
|
||||
|
||||
static void DeleteSlabAllocator(void*) {
|
||||
delete slab_allocator;
|
||||
slab_allocator = NULL;
|
||||
}
|
||||
|
||||
|
||||
void StreamWrap::Initialize(Handle<Object> target) {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
|
||||
slab_allocator = new SlabAllocator(SLAB_SIZE);
|
||||
AtExit(DeleteSlabAllocator, NULL);
|
||||
|
||||
HandleScope scope(node_isolate);
|
||||
|
||||
HandleWrap::Initialize(target);
|
||||
@ -592,8 +580,7 @@ void StreamWrapCallbacks::AfterWrite(WriteWrap* w) {
|
||||
|
||||
uv_buf_t StreamWrapCallbacks::DoAlloc(uv_handle_t* handle,
|
||||
size_t suggested_size) {
|
||||
char* buf = slab_allocator->Allocate(wrap_->object_, suggested_size);
|
||||
return uv_buf_init(buf, suggested_size);
|
||||
return uv_buf_init(new char[suggested_size], suggested_size);
|
||||
}
|
||||
|
||||
|
||||
@ -604,26 +591,30 @@ void StreamWrapCallbacks::DoRead(uv_stream_t* handle,
|
||||
HandleScope scope(node_isolate);
|
||||
|
||||
if (nread < 0) {
|
||||
// If libuv reports an error or EOF it *may* give us a buffer back. In that
|
||||
// case, return the space to the slab.
|
||||
if (buf.base != NULL)
|
||||
slab_allocator->Shrink(Self(), buf.base, 0);
|
||||
delete[] buf.base;
|
||||
|
||||
SetErrno(uv_last_error(uv_default_loop()));
|
||||
MakeCallback(Self(), onread_sym, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Local<Object> slab = slab_allocator->Shrink(wrap_->object_, buf.base, nread);
|
||||
if (nread == 0) {
|
||||
if (buf.base != NULL)
|
||||
delete[] buf.base;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(trevnorris): not kosher to use new/delete w/ realloc
|
||||
buf.base = static_cast<char*>(realloc(buf.base, nread));
|
||||
|
||||
if (nread == 0) return;
|
||||
assert(static_cast<size_t>(nread) <= buf.len);
|
||||
|
||||
int argc = 3;
|
||||
Local<Value> argv[4] = {
|
||||
slab,
|
||||
Integer::NewFromUnsigned(buf.base - Buffer::Data(slab), node_isolate),
|
||||
Integer::NewFromUnsigned(nread, node_isolate)
|
||||
Buffer::Use(buf.base, nread),
|
||||
Uint32::New(0, node_isolate),
|
||||
Uint32::New(nread, node_isolate)
|
||||
};
|
||||
|
||||
Local<Object> pending_obj;
|
||||
|
@ -135,8 +135,6 @@ class StreamWrap : public HandleWrap {
|
||||
void UpdateWriteQueueSize();
|
||||
|
||||
private:
|
||||
static inline char* NewSlab(v8::Handle<v8::Object> global, v8::Handle<v8::Object> wrap_obj);
|
||||
|
||||
// Callbacks for libuv
|
||||
static void AfterWrite(uv_write_t* req, int status);
|
||||
static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size);
|
||||
@ -151,7 +149,6 @@ class StreamWrap : public HandleWrap {
|
||||
template <enum encoding encoding>
|
||||
static v8::Handle<v8::Value> WriteStringImpl(const v8::Arguments& args);
|
||||
|
||||
size_t slab_offset_;
|
||||
uv_stream_t* stream_;
|
||||
|
||||
StreamWrapCallbacks default_callbacks_;
|
||||
|
@ -21,15 +21,12 @@
|
||||
|
||||
#include "node.h"
|
||||
#include "node_buffer.h"
|
||||
#include "slab_allocator.h"
|
||||
#include "req_wrap.h"
|
||||
#include "handle_wrap.h"
|
||||
#include "udp_wrap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SLAB_SIZE (1024 * 1024)
|
||||
|
||||
|
||||
namespace node {
|
||||
|
||||
@ -45,6 +42,7 @@ using v8::Object;
|
||||
using v8::Persistent;
|
||||
using v8::PropertyAttribute;
|
||||
using v8::String;
|
||||
using v8::Uint32;
|
||||
using v8::Value;
|
||||
|
||||
typedef ReqWrap<uv_udp_send_t> SendWrap;
|
||||
@ -56,13 +54,6 @@ static Persistent<Function> constructor;
|
||||
static Persistent<String> buffer_sym;
|
||||
static Persistent<String> oncomplete_sym;
|
||||
static Persistent<String> onmessage_sym;
|
||||
static SlabAllocator* slab_allocator;
|
||||
|
||||
|
||||
static void DeleteSlabAllocator(void*) {
|
||||
delete slab_allocator;
|
||||
slab_allocator = NULL;
|
||||
}
|
||||
|
||||
|
||||
UDPWrap::UDPWrap(Handle<Object> object)
|
||||
@ -79,9 +70,6 @@ UDPWrap::~UDPWrap() {
|
||||
void UDPWrap::Initialize(Handle<Object> target) {
|
||||
HandleWrap::Initialize(target);
|
||||
|
||||
slab_allocator = new SlabAllocator(SLAB_SIZE);
|
||||
AtExit(DeleteSlabAllocator, NULL);
|
||||
|
||||
HandleScope scope(node_isolate);
|
||||
|
||||
buffer_sym = NODE_PSYMBOL("buffer");
|
||||
@ -383,9 +371,7 @@ void UDPWrap::OnSend(uv_udp_send_t* req, int status) {
|
||||
|
||||
|
||||
uv_buf_t UDPWrap::OnAlloc(uv_handle_t* handle, size_t suggested_size) {
|
||||
UDPWrap* wrap = static_cast<UDPWrap*>(handle->data);
|
||||
char* buf = slab_allocator->Allocate(wrap->object_, suggested_size);
|
||||
return uv_buf_init(buf, suggested_size);
|
||||
return uv_buf_init(new char[suggested_size], suggested_size);
|
||||
}
|
||||
|
||||
|
||||
@ -397,23 +383,30 @@ void UDPWrap::OnRecv(uv_udp_t* handle,
|
||||
HandleScope scope(node_isolate);
|
||||
|
||||
UDPWrap* wrap = reinterpret_cast<UDPWrap*>(handle->data);
|
||||
Local<Object> slab = slab_allocator->Shrink(wrap->object_,
|
||||
buf.base,
|
||||
nread < 0 ? 0 : nread);
|
||||
if (nread == 0) return;
|
||||
|
||||
if (nread < 0) {
|
||||
if (buf.base != NULL)
|
||||
delete[] buf.base;
|
||||
Local<Value> argv[] = { Local<Object>::New(node_isolate, wrap->object_) };
|
||||
SetErrno(uv_last_error(uv_default_loop()));
|
||||
MakeCallback(wrap->object_, onmessage_sym, ARRAY_SIZE(argv), argv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nread == 0) {
|
||||
if (buf.base != NULL)
|
||||
delete[] buf.base;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(trevnorris): not kosher to use new/delete w/ realloc
|
||||
buf.base = static_cast<char*>(realloc(buf.base, nread));
|
||||
|
||||
Local<Value> argv[] = {
|
||||
Local<Object>::New(node_isolate, wrap->object_),
|
||||
slab,
|
||||
Integer::NewFromUnsigned(buf.base - Buffer::Data(slab), node_isolate),
|
||||
Integer::NewFromUnsigned(nread, node_isolate),
|
||||
Buffer::Use(buf.base, nread),
|
||||
Uint32::New(0, node_isolate),
|
||||
Uint32::New(nread, node_isolate),
|
||||
AddressToJS(addr)
|
||||
};
|
||||
MakeCallback(wrap->object_, onmessage_sym, ARRAY_SIZE(argv), argv);
|
||||
|
Loading…
x
Reference in New Issue
Block a user