在Nodejs中,为什么递归和非递归对象遍历在这种情况下几乎相同

时间:2015-07-31 10:43:37

标签: javascript node.js performance recursion v8

以下是我的比较代码。我正在为我的项目做一些优化,发现非递归解决方案没有优于递归解决方案,这违背了我的直觉:性能几乎是线性相同的。

我想知道这个案例是否是特定的案例,或者有一些关于我不知道的v8引擎的细节。

编辑:

我已将随机对象的生成放在循环之外,它们几乎是线性相同的。

"use restrict";

if (!Array.prototype.fill) {
    Array.prototype.fill = function(content) {
        for (var i = 0; i < this.length; i++) {
            this[i] = content;
        }
    };
}

var getClassName = (function () {

    var NULL_OBJECT = {};

    return function(obj) {
        return NULL_OBJECT.toString.apply(obj).slice(8, -1);
    }
})();

var Stack = function(opts) {
    opts = opts || {};
    var capacity = opts.capacity;
    if (!capacity) {
        capacity = 64;
    }
    this.capacity = capacity;
    this.list = new Array(this.capacity);
    this.cursor = 0;
};

Stack.prototype.__doubleTheSize = function() {
    this.list = this.list.concat(new Array(this.capacity));
    this.capacity = this.capacity * 2;
};

Stack.prototype.push = function(obj) {
    if (this.cursor == this.capacity - 1) {
        this.__doubleTheSize();
    }
    this.list[this.cursor] = obj;
    this.cursor += 1;
};

Stack.prototype.clear = function() {
    this.cursor = 0;
    this.list.fill(undefined);
};

Stack.prototype.pop = function() {
    if (this.cursor == 0) {
        throw new Error('Stack is empty, pop is illegal.');
    }
    var val = this.list[this.cursor - 1];
    this.list[this.cursor - 1] = undefined;
    this.cursor -= 1;
    return val;
};

function traverseRecursivly(obj, funcVisit) {

    var className = getClassName(obj);
    funcVisit(obj);
    if (className != 'Object' && className != 'Array') {
        return;
    }
    for (var i in obj) {
        traverseRecursivly(obj[i], funcVisit);
    }
}

var traverseNonRecursivly = (function() {

    var stack = new Stack({
        capacity: 1024
    });

    return function(obj, funcVisit) {

        var __obj = obj;
        stack.push(__obj);
        while (stack.cursor > 0) {
            __obj = stack.pop();
            var className = getClassName(__obj);
            funcVisit(__obj);
            if (className != 'Object' && className != 'Array') {
                continue;
            }
            for (var i in __obj) {
                stack.push(__obj[i]);
            }
        }
    };
})();

var genRandomTreeObject = (function() {

    var stack = new Stack();

    return function(size) {

        var head = {
            value: 0
        };
        stack.push(head);

        for (var i = 1; i <= size; i++) {
            var node = {
                value: i
            };

            var parent = stack.list[Math.floor(Math.random() * stack.cursor)];
            parent[i] = node;
            stack.push(node);
        }

        stack.clear();
        return head;
    }

})();

var randomObject = genRandomTreeObject(1024);

console.time('recursive');
for (var i = 0; i < 1000; i++) {
    var sum = 0;
    traverseNonRecursivly(randomObject, function(o) {
        if (o.value) {
            sum += o.value;
        }
    })
}
console.log(sum);
console.timeEnd('recursive');

console.time('non-recursive');
for (var i = 0; i < 1000; i++) {
    var sum = 0;
    traverseRecursivly(randomObject, function(o) {
        if (o.value) {
            sum += o.value;
        }
    })
}
console.log(sum);
console.timeEnd('non-recursive');

1 个答案:

答案 0 :(得分:0)

这是正确的(见问题评论)。我进行了分析,结果显示只有4%的时间用在traverse[Non]Recursivly()中,而超过83%的时间用在C ++运行时,即v8::internal::JSObject::GetOwnElementKeys()(&gt; 57%)。

[JavaScript]:
ticks  total  nonlib   name
  98    4.0%    4.0%  LazyCompile: ~traverseRecursivly

  ... 

[C++]:
ticks  total  nonlib   name
 1399   57.6%   57.7%  v8::internal::JSObject::GetOwnElementKeys(...)

  ...

[Summary]:
  ticks  total  nonlib   name
   389   16.0%   16.0%  JavaScript
  2037   83.8%   84.0%  C++
    11    0.5%    0.5%  GC
     4    0.2%          Shared libraries

[C++ entry points]:
  ticks    cpp   total   name
  1665   82.4%   68.5%  v8::internal::Runtime_GetPropertyNamesFast(...)
   207   10.2%    8.5%  v8::internal::Runtime_SubStringRT(...)
  ...

black color for code kind in execution means runtime