Node.js Undocumented(1) Node.js Undocumented(2) 寫(xiě)這種系列blog,是為了監(jiān)督自己,不然我估計(jì)我不會(huì)有動(dòng)力寫(xiě)完。這一節(jié),我將介紹下Buffer這個(gè)module。js本身對(duì)文本友好,但是處理二進(jìn)制數(shù)據(jù)就不是特別方便,因此node.js提供了Buffer模塊來(lái)幫助你處理二進(jìn)制數(shù)據(jù),畢竟node.js的定位在網(wǎng)絡(luò)服務(wù)端,不能只對(duì)文本協(xié)議友好。
Buffer模塊本身其實(shí)沒(méi)有多少未公開(kāi)的方法,重要的方法都在文檔里提到了,有兩個(gè)方法稍微值的提下。
Buffer.get(idx) 跟buffer[idx]是一樣的,返回的是第idx個(gè)字節(jié),返回的結(jié)果是數(shù)字,如果要轉(zhuǎn)成字符,用String.fromCharCode(code)即可。
Buffer.inspect() 返回Buffer的字符串表示,每個(gè)字節(jié)用十六進(jìn)制表示,當(dāng)你調(diào)用console.dir的時(shí)候打印的就是這個(gè)方法返回的結(jié)果。
Buffer真正值的一提的是它的內(nèi)部實(shí)現(xiàn)。Buffer在node.js內(nèi)部的cpp代碼對(duì)應(yīng)的是SlowBuffer類(lèi)(src/node_buffer.cc),但是兩者之間并不是一一對(duì)應(yīng)。
對(duì)于創(chuàng)建小于8K的Buffer,其實(shí)是從一個(gè)pool里slice出來(lái),只有大于8K的Buffer才是每次都new一個(gè)SlowBuffer。查看源碼(lib/buffer.js):
Buffer.poolSize = 8 * 1024;
if (this.length > Buffer.poolSize) {
// Big buffer, just alloc one.
this.parent = new SlowBuffer(this.length);
this.offset = 0;
} else {
// Small buffer.
if (!pool || pool.length - pool.used < this.length) allocPool();
this.parent = pool;
this.offset = pool.used;
pool.used += this.length;
}
因此,我們可以修改Buffer.poolSize這個(gè)“靜態(tài)”變量來(lái)改變池的大小
Buffer.poolSize Buffer類(lèi)創(chuàng)建的池大小,大于此值則每次new一個(gè)SlowBuffer,否則從池中slice返回一個(gè)Buffer,如果池剩余空間不夠,則新創(chuàng)建一個(gè)SlowBuffer做為池。下面的例子打印這個(gè)值并修改成16K:
console.log(Buffer.poolSize);
Buffer.poolSize=16*1024;
SlowBuffer類(lèi) SlowBuffer類(lèi)我們可以直接使用的,如果你不想使用Buffer類(lèi)的話,SlowBuffer類(lèi)有Buffer模塊的所有方法實(shí)現(xiàn),例子如下:
var SlowBuffer=require('buffer').SlowBuffer
var buf=new SlowBuffer(1024)
buf.write("hello",'utf-8');
console.log(buf.toString('utf-8',0,5));
console.log(buf[0]);
var sub=buf.slice(1,3);
console.log(sub.length);
請(qǐng)注意,SlowBuffer默認(rèn)不是Global的,需要require buffer模塊。
使用建議和性能測(cè)試 Buffer的這個(gè)實(shí)現(xiàn)告訴我們,要使用好Buffer類(lèi)還是有講究的,每次創(chuàng)建小于8K的Buffer最好大小剛好能被8k整除,這樣能充分利用空間;或者每次創(chuàng)建大于8K的Buffer,并充分重用。我們來(lái)看一個(gè)性能測(cè)試,分別循環(huán)1000萬(wàn)次創(chuàng)建16K,4096和4097大小的Buffer,看看耗時(shí)多少:
function benchmark(size,repeats){
var total=0;
console.log("create %d size buffer for %d times",size,repeats);
console.time("times");
for(var i=0;i<repeats;i++){
total+=new Buffer(size).length;
}
console.timeEnd("times");
}
var repeats=10000000;
console.log("warm up
")
benchmark(1024,repeats);
console.log("start benchmark")
benchmark(16*1024,repeats);
benchmark(4096,repeats);
benchmark(4097,repeats);
創(chuàng)建1024的Buffer是為了做warm up。在我機(jī)器上的輸出:
start benchmark
create 16384 size buffer for 10000000 times
times: 81973ms
create 4096 size buffer for 10000000 times
times: 80452ms
create 4097 size buffer for 10000000 times
times: 138364ms
創(chuàng)建4096和創(chuàng)建4097大小的Buffer,只差了一個(gè)字節(jié),
耗時(shí)卻相差非常大,為什么會(huì)這樣?讀者可以自己根據(jù)上面的介紹分析下,有興趣的可以留言。
另外,可以看到創(chuàng)建16K和創(chuàng)建4K大小的Buffer,差距非常小,平均每秒鐘都能創(chuàng)建10萬(wàn)個(gè)以上的Buffer,這個(gè)效率已經(jīng)足以滿(mǎn)足絕大多數(shù)網(wǎng)絡(luò)應(yīng)用的需求。