2016-07-16 160 views
6

Desidero estendere il prototipo di oggetto String con un metodo di utilità. Ha funzionato, ma la performance è stata sorprendentemente bassa. Passare una stringa a una funzione è 10 volte più veloce rispetto a sostituire il metodo String.prototype che sta facendo la stessa cosa. Per assicurarmi che ciò accada davvero, ho creato una semplice funzione count() e i metodi corrispondenti.L'estensione delle prestazioni string.prototype mostra che le chiamate di funzione sono 10 volte più veloci

(stavo sperimentando, e ha creato tre diverse versioni del metodo.)

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') 

Risultati:

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

Come si può vedere la differenza è drammatica.

Il seguente dimostra che le prestazioni delle chiamate di metodo sono trascurabilmente più lente e che il codice funzione è autonomo per per i metodi.

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') 

Risultati:

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

Anche se su enormi ripetizioni (come 1e8) prototipo metodi dimostra di essere 10x volte più lento di funzioni, questo possono essere ignorati per questo caso.

Tutto questo potrebbe essere correlato solo a un oggetto String, perché semplici oggetti generici eseguono circa lo stesso quando li si passa a funzioni o chiamare i loro metodi:

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') 

risultati:

func: 1689.942ms 
method: 1674.639ms 

Ho cercato su StackOverflow e binging, ma altri che il consiglio "non estendere String o Array perché inquini lo spazio dei nomi" (che non è un problema per il mio particolare progetto), non riesco a trovare nulla relativo alle prestazioni dei metodi rispetto a functio ns. Quindi dovrei semplicemente dimenticare di estendere l'oggetto String a causa di un calo delle prestazioni dei metodi aggiunti o c'è dell'altro?

+0

Grazie per le informazioni, ma, tu sei l'ultima realizzazione è lontano da 'metodo prototype' si può sostituire con la funzione di oggetto e quindi aggiungere prototipo. quindi ottenere una nuova istanza di esso. Il risultato BTW è lo stesso. –

risposta

5

Ciò è molto probabile perché non si utilizza la modalità rigorosa e il valore this all'interno del metodo deve essere inoltrato a un'istanza String anziché essere una stringa primitiva.

È possibile confermare ripetendo la misurazione su var STR = new String('01101011…').

quindi fissare l'implementazione:

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; 
}; 
+1

Funziona, ma non ha senso per me! Così ho aggiunto una nuova domanda: http://stackoverflow.com/questions/38411552/why-use-strict-improves-performance-10x-in-this-example – exebook

+2

@exebook: Come ho detto, la modalità sloppy lancia "questo 'valore a un oggetto, quindi è necessario creare un'istanza' String' su ogni chiamata, che è un po 'di overhead per un metodo così semplice come il tuo – Bergi