繁体   English   中英

扩展 String.prototype 性能表明函数调用速度提高了 10 倍

[英]Extending String.prototype performance shows that function calls are 10x faster

我想用一些实用方法扩展 String 对象原型。 它奏效了,但性能却出奇地低。 将字符串传递给函数比覆盖执行相同操作的String.prototype方法快 10 倍。 为了确保这真的发生,我创建了一个非常简单的count()函数和相应的方法。

(我正在试验,并创建了该方法的三个不同版本。)

function count(str, char) {
    var n = 0;
    for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
    return n;
}

String.prototype.count = function (char) {
    var n = 0;
    for (var i = 0; i < this.length; i++) if (this[i] == char) n++;
    return n;
}

String.prototype.count_reuse = function (char) {
    return count(this, char)
}

String.prototype.count_var = function (char) {
    var str = this;
    var n = 0;
    for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
    return n;
}

// Here is how I measued speed, using Node.js 6.1.0

var STR ='0110101110010110100111010011101010101111110001010110010101011101101010101010111111000';
var REP = 1e3//6;

console.time('func')
for (var i = 0; i < REP; i++) count(STR,'1')
console.timeEnd('func')

console.time('proto')
for (var i = 0; i < REP; i++) STR.count('1')
console.timeEnd('proto')

console.time('proto-reuse')
for (var i = 0; i < REP; i++) STR.count_reuse('1')
console.timeEnd('proto-reuse')

console.time('proto-var')
for (var i = 0; i < REP; i++) STR.count_var('1')
console.timeEnd('proto-var')

结果:

func: 705 ms
proto: 10011 ms
proto-reuse: 10366 ms
proto-var: 9703 ms

如您所见,差异是巨大的。

下面证明方法调用的性能可以忽略不计,并且方法本身的函数代码慢。

function count_dummy(str, char) {
    return 1234;
}

String.prototype.count_dummy = function (char) {
    return 1234; // Just to prove that accessing the method is not the bottle-neck.
}

console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')

console.time('proto-dummy')
for (var i = 0; i < REP; i++) STR.count_dummy('1')
console.timeEnd('proto-dummy')

console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')

结果:

func-dummy: 0.165ms
proto-dummy: 0.247ms

尽管在大量重复(如 1e8)中,原型方法被证明比函数慢 10 倍,但对于这种情况可以忽略。

所有这些可能只与 String 对象有关,因为简单的通用对象在您将它们传递给函数或调用它们的方法时的表现大致相同:

var A = { count: 1234 };

function getCount(obj) { return obj.count }

A.getCount = function() { return this.count }

console.time('func')
for (var i = 0; i < 1e9; i++) getCount(A)
console.timeEnd('func')

console.time('method')
for (var i = 0; i < 1e9; i++) A.getCount()
console.timeEnd('method')

结果:

func: 1689.942ms
method: 1674.639ms

我一直在搜索 Stackoverflow 和 binging,但除了建议“不要扩展字符串或数组,因为你会污染名称空间”(这对我的特定项目来说不是问题),我找不到任何与方法性能相关的内容与函数相比。 那么我是否应该因为添加方法的性能下降而忘记扩展 String 对象,或者还有更多关于它的内容?

这很可能是因为您没有使用严格模式,并且您的方法中的this值被强制转换为String实例而不是原始字符串。 这种强制以及对String对象的进一步方法调用或属性访问比使用原始值慢。

您可以(编辑:至少可以在 2016 年)通过对var STR = new String('01101011…')重复测量来确认这一点,这应该会减少开销。

然后修复您的实现:

String.prototype.count = function (char) {
    "use strict";
//  ^^^^^^^^^^^^
    var n = 0;
    for (var i = 0; i < this.length; i++)
        if (this[i] == char)
            n++;
    return n;
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM