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:
Trevor Norris 2013-06-19 11:20:45 -07:00
parent c1db1ecd15
commit ec90e6e80a
6 changed files with 30 additions and 227 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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