浅析node的buffer模块(一创建)
by snoopyxdy
at 2013-04-19 10:55:25
original http://snoopyxdy.blog.163.com/blog/static/60117440201331683752285
if (type === 'string') {// We are a stringthis.length = this.write(subject, 0, encoding); //将字符串写入}
else if (Buffer.isBuffer(subject)) {if (subject.parent)subject.parent.copy(this.parent,this.offset,subject.offset,this.length + subject.offset);elsesubject.copy(this.parent, this.offset, 0, this.length);}
else if (isArrayIsh(subject)) {for (var i = 0; i < this.length; i++)this.parent[i + this.offset] = subject[i];}
function isArrayIsh(subject) { return Array.isArray(subject) || subject && typeof subject === 'object' && typeof subject.length === 'number'; }
this.parent = new SlowBuffer(this.length);
function allocPool() {pool = new SlowBuffer(Buffer.poolSize);pool.used = 0;}buffer类代码:if (!pool || pool.length - pool.used < this.length) allocPool();
this.parent = pool;
static void Initialize(v8::Handle<v8::Object> target); //初始化函数
// copy free
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Slice", Buffer::Ucs2Slice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexSlice", Buffer::HexSlice);
// TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
// copy
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Write", Buffer::Ucs2Write);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexWrite", Buffer::HexWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatLE", Buffer::ReadFloatLE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatBE", Buffer::ReadFloatBE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readDoubleLE", Buffer::ReadDoubleLE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readDoubleBE", Buffer::ReadDoubleBE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeFloatLE", Buffer::WriteFloatLE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeFloatBE", Buffer::WriteFloatBE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeDoubleLE", Buffer::WriteDoubleLE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "writeDoubleBE", Buffer::WriteDoubleBE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "fill", Buffer::Fill);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy);
NODE_SET_METHOD(constructor_template->GetFunction(),
"byteLength",
Buffer::ByteLength);
NODE_SET_METHOD(constructor_template->GetFunction(),
"makeFastBuffer",
Buffer::MakeFastBuffer);
当node调用 var buf = new Buffer()
Handle<Value> Buffer::New(const Arguments &args) {if (!args.IsConstructCall()) { //如果不是构造函数调用,如果不是,则使用构造函数调用return FromConstructorTemplate(constructor_template, args);}HandleScope scope;if (!args[0]->IsUint32()) return ThrowTypeError("Bad argument");size_t length = args[0]->Uint32Value();if (length > Buffer::kMaxLength) {return ThrowRangeError("length > kMaxLength");}new Buffer(args.This(), length);return args.This();}
Buffer::Buffer(Handle<Object> wrapper, size_t length) : ObjectWrap() {Wrap(wrapper);length_ = 0;callback_ = NULL;handle_.SetWrapperClassId(BUFFER_CLASS_ID);//定义包装的对象ID,检查堆的运行情况,初始化时会去定义这个堆的id和回调函数Replace(NULL, length, NULL, NULL);}
// if replace doesn't have a callback, data must be copied
// const_cast in Buffer::New requires this
void Buffer::Replace(char *data, size_t length,
free_callback callback, void *hint) {
HandleScope scope;
if (callback_) {//非初始化执行
callback_(data_, callback_hint_);
} else if (length_) {
delete [] data_;
V8::AdjustAmountOfExternalAllocatedMemory(
-static_cast<intptr_t>(sizeof(Buffer) + length_));
}
length_ = length;
callback_ = callback;
callback_hint_ = hint;
if (callback_) { //初始化不执行
data_ = data;
} else if (length_) { //初始化执行
data_ = new char[length_]; //将data_指针指向char[length]
if (data) //参数传递了
memcpy(data_, data, length_); //从data内存指针拷贝length长度的字节到data_指针指向的内存中
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer) + length_);//调用V8调整外部内存大小的
//文档上说注册更多的外部内存会让V8的GC更加活跃
//当然从这点我们就可以发现,slowbuffer的创建确实会有消耗
} else {
data_ = NULL;
}
handle_->SetIndexedPropertiesToExternalArrayData(data_,
kExternalUnsignedByteArray,
length_);//SetIndexedPropertiesToExternalArrayData表示将js对象的内存地址通过V8做好关联,当js对象失去对这个地址的访问
//v8引起将delete这个data_ 指针。
handle_->Set(length_symbol, Integer::NewFromUnsigned(length_));
//关于这个handle_是在node_object_wrap.h文件中的ObjectWrap类定义的
//v8::Persistent<v8::Object> handle_; // ro 至于 Persistent 和 handle 的区别,cnode上有一篇文章介绍的很详细,
//简单点说就是:handle是栈,Persistent是堆
//最后这行表示设置这个对象的属性length
//length_symbol 表示length ,见代码:
// length_symbol = NODE_PSYMBOL("length");
}
static intptr_t v8::V8::AdjustAmountOfExternalAllocatedMemory | ( | intptr_t | change_in_bytes | ) | [static] |
Deprecated. Use Isolate::AdjustAmountOfExternalAllocatedMemory instead.
var time = 10*10000; //10万次
console.time('1024*4')
for(var i=0;i<time;i++)
var x = new Buffer(1024*4);
console.timeEnd('1024*4')
console.time('1024*4+1')
for(var j=0;j<time;j++)
var y = new Buffer(1024*4+1);
console.timeEnd('1024*4+1')
1024*4: 337ms
1024*4+1: 615ms
当然这也算8KB的一个注意点,但是从中我们不难发现,重新申请一份额外的内存空间的消耗是挺大的。
var time = 10*10000;
var str = '1';
var max = 2048;
console.time('many buffer')
var ary1=[]
for(var i=0;i<time;i++){
var tempi = Math.ceil(Math.random()*max)
var tempstr = str
while(tempi--){
tempstr += str
}
ary1.push(new Buffer(tempstr))
}
console.timeEnd('many buffer')
console.time('one buffer')
var ary_offset=[];
var ary_len=[];
var tempbuf = new Buffer(time*max)
var offset = 0;
for(var i=0;i<time;i++){
var len;
var tempi = len = Math.ceil(Math.random()*max)
var tempstr = str
while(tempi--){
tempstr += str
}
var end = offset+len
tempbuf.fill(tempstr, offset, end)
ary_offset.push(offset)
ary_len.push(end)
offset = end
}
console.timeEnd('one buffer')
console.time('many buffer read')
for(var x=0;x<100000;x++){
ary1[x].toString('utf-8')
}
console.timeEnd('many buffer read')
console.time('one buffer read')
for(var y=0;y<100000;y++){
tempbuf.toString('utf-8', ary_offset[y], ary_len[y])
}
console.timeEnd('one buffer read')
many buffer: 4622ms
one buffer: 2942ms
many buffer read: 92ms
one buffer read: 91ms
对于内存的消耗和执行的时间取舍我们要根据实际情况来取舍了。
如果真的有必要创建很多buffer对象,不如创建一个大的buffer,然后记录每块使用的偏移这样比生成很多小buffer要快很多。