¿Por qué el operador de comparación funciona más rápido independientemente de la longitud de la cadena?
Considere las siguientes dos funciones que uso para comparar cadenas con una longitud de 100 millones:
Con === :
// O(?) const compare1(a, b) { return a === b; }Con comparación carácter por carácter:
// O(N) const compare2(a, b) { if(a.length !== b.length) return false; const n = Math.max(a.length, b.length); for(var i=0; i<n; i++){ if(a[i] !== b[i]) return false; } return true; } Al probar estas dos funciones, encuentro que la velocidad de la función compare1 es significativamente más rápida.
En el caso de la función compare2 , creo que la sobrecarga será severa al interpretar el código JavaScript, acceder y comparar la memoria.
Pero según tengo entendido, la función compare1 también puede tener que comparar N caracteres. ¿Funciona mucho más rápido porque todo sucede en un nivel inferior?
Hay dos consideraciones:
=== la comparación de primitivas de cadena se implementa con código compilado de nivel inferior, que de hecho se ejecuta más rápido que el bucle de JavaScript explícito, que tiene trabajo adicional, incluida la actualización de una variable de JavaScript ( i ), realizando un acceso con esa variable ( a[i] ); donde se deben seguir todos los procedimientos ECMAScript prescritos.
El motor de JavaScript puede optimizar el uso de la memoria y usar el conocimiento de que dos cadenas son iguales (por ejemplo, cuando eso ya se detectó en el momento del análisis, o una cadena se asigna a una segunda variable/propiedad) y solo almacena esa cadena una vez (cf. grupo de cuerdas). En ese caso, la comparación es una comparación O(1) trivial de dos referencias. Sin embargo, en JavaScript no hay forma de inspeccionar si dos cadenas primitivas realmente comparten la misma memoria.
Como ilustración del segundo punto, observe cómo el tiempo de comparación es diferente para dos casos de comparación de cadenas largas que son iguales, lo que probablemente sea una pista de que está ocurriendo esta combinación de cadenas:
function compare(a, b) { let sum = 0, start, p; for (let i = 0; i < 10; i++) { // Perform 10 comparisons start = performance.now(); p = a === b; sum += performance.now() - start; } return sum / 10; // Return average time to make the comparison } console.log("Hold on while strings are being created..."); setTimeout(function () { // Create long, non-trivial string let a = Array.from({length: 10000000}, (_, i) => i).join(""); let b = a.slice(0); // Plain Copy - engine realises it is the same string & does not allocate space let c = a[0] + a.slice(1); // More complex - engine does not realise it is the same string console.log("Strings created. Test whether they are equal:", a === b && b === c); console.log(compare(a, b) + "ms"); console.log(compare(a, c) + "ms"); });