刚开始接触nodejs,做这个测试目的是为了
1、熟悉nodejs的语法 2、比较异步和同步实现的性能差异
3、比较nodejs和java实现的性能差异
实现功能: 统计文件系统中某个目录的大小。
运行环境: winxp nodejs 0.6.17 jdk1.6.0_10-rc2
代码见附件: 代码1、syncDirTree.js 同步 代码2、asyncDirTree.js 小范围实现异步 代码3、asyncDirTree1.js 全部异步实现
var fs = require('fs');
var Path = require('path');
var queue = new Array();
var root = "d:\\tools";
var tree = {};
function TreeNode(filename){
this.path = filename;
this.children = [];
this.isCaculating = true;
this.size = 0;
this.childrenNum = 0;
this._childBeenAdded = 0;
this._callback;
this.expand = function(callback){
var fs_stat;
try{
fs_stat =fs.statSync(this.path);
}catch(e){
callback(new Error(e),0);
return;
}
if(!fs_stat.isDirectory()){ //has no children
this.isCaculating = false;
this.size = fs_stat.size;
callback(null, fs_stat.size);
}else{
var me = this;
this.callback = callback;
fs.readdir(this.path, function(err, files){
me.childrenNum = files.length;
if(files.length==0){ //empty folder
me.isCaculating = false;
me.callback(null,0);
return;
}
files.forEach(function(file){
var _filename =Path.join(me.path,file);
var _treeNode = new TreeNode(_filename);
//console.log(this);
me.children.push(_treeNode);
_treeNode.expand(function(err,size){
me.size += size;
me._childBeenAdded++;
if(me._childBeenAdded >= me.childrenNum){
me.isCaculating = false;
me.callback(null,me.size);
}
})
});
})
}
}
}
var rootNode = new TreeNode(root);
var begin = new Date().getTime();
rootNode.expand(function(err,size){
if(!err){
var end = new Date().getTime();
console.log('elapse time: '+ (end - begin));
console.log('size:' + size);
}
})
代码4、FileSize.java java实现
public class FileSize {
public long expand(File file){
if(!file.isDirectory()){
//this.size = file.length();
return file.length();
}
File[] files = file.listFiles();
if(files.length == 0)return 0;
long _size =0;
for(int i=0;i<files.length;i++){
_size += expand(files[i]);
}
return _size;
}
public static void main(String[] args) {
long begin = System.currentTimeMillis();
File file = new File("d:\\tools");
FileSize fs = new FileSize();
System.out.println( fs.expand(file) );
long end = System.currentTimeMillis();
System.out.println( end - begin );
}
测试目录为d:\tools,17,007,212,070 字节,19万个左右文件和目录
测试结果: 代码1-3 执行时间差不多,为22秒左右 代码4执行时间为9秒左右
结论: 文件操作占用了绝大部分时间,所以nodejs异步和同步实现的性能差异不大。 java的文件操作优于nodejs,速度快1倍。
测试有些粗陋,测试结果仅供参考。也请大家审查,多提意见。
4 回复
NodeJS的一大优势就是非阻塞的IO操作,即IO操作时立即返回干点儿别的活儿~ 但是就文中测试实例而言,几乎全是IO操作,所谓“别的活儿”也只是加加文件大小可以忽略不计~ 因此,异步与同步就区别不大了~
或者这时,多线程的优势就体现出来了(一个人可以模拟出N个人在干活儿)~
楼主的代码中,读取文件属性用的是阻塞函数fs.statSync()
,因此测试是不准确的。
且看以下代码:
var fs = require('fs');
var path = require('path');
var THREAD = require('os').cpus().length;
var expand = function (file, callback) {
fs.stat(file, function (err, stats) {
if (err)
return callback(err);
if (!stats.isDirectory()) {
return callback(null, stats.size);
} else {
fs.readdir(file, function (err, files) {
if (err)
return callback(err);
for (var i = 0, len = files.length; i < len; i++)
files[i] = path.join(file, files[i]);
var size = 0;
var finish = 0;
var threadFinish = function () {
finish++;
if (finish >= THREAD)
return callback(null, size);
};
var next = function () {
var f = files.pop();
if (!f)
return threadFinish();
expand(f, function (err, s) {
if (err)
return callback(err);
size += s;
next();
});
};
for (var i = 0; i < THREAD; i++)
next();
});
}
});
};
var ts = new Date();
expand('d:\\tools', function (err, size) {
if (err)
console.log(err.stack);
var te = new Date();
console.log('Size: ' + size);
console.log('Spent: ' + (te - ts));
});
运行结果:
Java程序需要6.8秒
楼主的Node.js程序需要27.4秒
经过改良后的Node.js程序需要10秒
结论:虽然Node.js程序比Java程序稍慢(这是正常的),但是差异并不像楼主所说的那么离谱。