Volver al Centro de conocimiento
Huella digital

Cómo hardwareConcurrency revela los núcleos reales de tu CPU

Descubre cómo los navegadores informan los núcleos de CPU con navigator.hardwareConcurrency y pruebas con Workers, y qué puede revelar esa señal.

BotBrowser Team

Documentación

Prefieres la documentación del producto mantenida?

Este artículo tiene una página equivalente en el centro de documentación. Usa los docs para el flujo canónico, las flags actuales y la referencia duradera.

navigator.hardwareConcurrency es un único número, pero es una de las pistas de hardware que cualquier script puede leer sin pedir permiso. Indica cuántos procesadores lógicos puede usar el navegador y está disponible en el hilo principal y en todos los tipos de worker. Un sitio que quiera comprobar si la descripción de un dispositivo es coherente puede comparar ese número con los demás valores de hardware que ve y con el comportamiento real del trabajo en paralelo. Las secciones siguientes tratan qué significa el valor, por qué cambia entre máquinas, cuánto aporta a la identificación y qué debe cumplir un número de núcleos coherente y basado en un perfil. BotBrowser puede informar un mismo número de núcleos del perfil de forma coherente en los contextos de una prueba controlada; no puede cambiar cuántos núcleos tiene la máquina anfitriona ni cómo usa un sitio ese valor.

Diagrama de un número de núcleos del perfil informado al hilo principal, Worker, SharedWorker y ServiceWorker, con escalado paralelo alineado en Linux y Windows

Ver el video

Este video en inglés pone a prueba cuatro afirmaciones sobre el número de núcleos informado en una página de laboratorio controlada: la lectura en la consola, los contextos de Worker, una prueba de escalado cronometrada en un host Ubuntu y lo que dice la publicación sobre un perfil. Muestra dos equipos, no una población.

Resumen rápido

  • navigator.hardwareConcurrency informa de procesadores lógicos, no de un rendimiento garantizado.
  • Las máquinas virtuales, los contenedores, la política del navegador y las CPU híbridas pueden cambiar el valor.
  • Compara el valor del hilo principal y de los workers con la plataforma y la memoria del perfil.
  • BotBrowser mantiene coherente el valor del perfil; Linux y Windows también pueden alinear la afinidad de CPU de los Worker.

Contenido de esta guía

Qué informa navigator.hardwareConcurrency

navigator.hardwareConcurrency puede reflejar un invitado limitado a 2 vCPU, en lugar del número de procesadores del host.

La propiedad devuelve un entero que cuenta los procesadores lógicos disponibles para ejecutar hilos. Los procesadores lógicos son los núcleos físicos multiplicados por los hilos de hardware por núcleo, de modo que un procesador de cuatro núcleos con multihilo simultáneo informa 8. MDN describe la propiedad como el número de procesadores lógicos disponibles para el navegador, y la especificación HTML de WHATWG añade que un agente de usuario debería inclinarse por mostrar el número real y usar un valor menor solo cuando exista un límite o cuando el objetivo sea reducir el fingerprinting.

// Hilo principal
console.log(navigator.hardwareConcurrency);
// Dentro de un worker dedicado, un worker compartido o un service worker
console.log(self.navigator.hardwareConcurrency);

El número es una pista de planificación, no una medida de velocidad. No indica qué tan rápido es un núcleo, si los núcleos son de eficiencia o de rendimiento, cuánta carga tiene la máquina en ese momento ni cuántos hilos programará realmente el sistema operativo para la página. Dos máquinas que informan 8 pueden comportarse de forma muy distinta con la misma carga paralela.

Las aplicaciones leen el valor por motivos razonables, como elegir el tamaño de un grupo de workers, así que los navegadores no pueden eliminarlo sin romper código que funciona. Además no requiere permiso y se expone a todos los tipos de worker. Esa combinación explica por qué el número sigue visible y por qué el objetivo práctico es la coherencia entre contextos, no la eliminación.

Para el código de una aplicación, el patrón seguro es tratar el valor como un límite superior para un grupo de workers, dejar capacidad al hilo principal y medir el rendimiento real antes de fijar un tamaño. Un grupo igual al número informado puede competir con el renderizado y la gestión de la entrada del usuario, y un grupo mayor rara vez ayuda en trabajo limitado por CPU, porque los hilos adicionales esperan un procesador. Ese mismo número nunca debería servir para decidir una autorización ni para justificar la recopilación de un perfil de hardware más detallado.

Por qué el número cambia de una máquina a otra

En Linux, un contenedor puede dar al navegador una asignación cpuset.cpus de 2 vCPU en vez del total 16-core del host.

Varios factores deciden lo que informa un navegador.

  • Modelo de procesador. El número de núcleos e hilos procede de la CPU. Muchos procesadores de escritorio habituales informan 8, 12 o 16 procesadores lógicos, mientras que las máquinas antiguas o de gama baja pueden informar 2 o 4. Apple silicon y otros diseños híbridos combinan núcleos de eficiencia y de rendimiento, y el total depende del chip.
  • Máquinas virtuales. Una máquina virtual informa las CPU virtuales que le asigna el hipervisor. Un host con 16 núcleos que da 2 CPU virtuales a un invitado muestra 2 dentro del invitado, sea cual sea el hardware subyacente.
  • Contenedores y límites del sistema operativo. Los entornos de contenedores y los ajustes del planificador pueden restringir cuántos procesadores puede usar un proceso. Que el navegador vea esa restricción depende de cómo se aplique el límite.
  • Política del navegador. La especificación permite que un agente de usuario informe un valor menor cuando fija límites o quiere reducir el fingerprinting, y algunos navegadores han experimentado con limitar o agrupar el número. Chromium informa actualmente el número que expone el sistema operativo.

Esta variación afecta primero a los desarrolladores. El valor leído dentro de un contenedor de CI o de una instancia en la nube suele ser muy distinto del que da el mismo navegador en un portátil, así que una prueba que supone un número concreto puede pasar en un sitio y fallar en otro. Lee el número en tiempo de ejecución y trátalo como una entrada, nunca como una constante.

Los valores típicos siguen al mercado de hardware. En escritorio, 4 y 8 son los más comunes, y 6, 12 y 16 aparecen en familias concretas de procesadores. Por encima de 16 se ve sobre todo en estaciones de trabajo y servidores. Los teléfonos suelen informar 4, 6 u 8, según el diseño del chip. Los números impares como 7 o 13 son raros entre los procesadores que se venden, algo que cobra importancia cuando alguien se plantea elegir un número a mano.

Cuando una carga de trabajo pasa de un entorno a otro, compara el número informado y no el tamaño de instancia anunciado. Una instancia en la nube vendida con cierto número de CPU virtuales puede informar un valor distinto dentro de un contenedor, y un portátil con núcleos de eficiencia puede informar más procesadores lógicos de los que sugiere una lectura rápida de su ficha técnica. Registrar el valor observado junto a cada resultado de benchmark elimina una fuente habitual de confusión cuando dos ejecuciones difieren. En una máquina virtual, el valor informado también importa para la comprobación de escalado que se describe más abajo, porque un invitado con 2 CPU virtuales no puede ejecutar de verdad más de dos hilos a la vez. Un entorno coherente es aquel en el que el número informado y los procesadores disponibles coinciden.

Cuánto aporta la señal a la identificación

Los estudios de fingerprinting miden cuánto reduce cada atributo una población. Los datos de AmIUnique sitúan navigator.hardwareConcurrency en unos 4,5 bits de entropía, lo que equivale a separar unos 23 grupos igual de probables. La distribución real está lejos de ser uniforme: unos pocos valores comunes cubren la mayoría de los navegadores y el resto son raros, así que la división práctica es más gruesa de lo que sugiere la cifra.

Por sí solo, el valor es una señal que contribuye, no un identificador único. La entropía se acumula cuando los atributos son independientes. Cada bit adicional reduce aproximadamente a la mitad el conjunto de navegadores coincidentes, de modo que un número de núcleos combinado con un tamaño de pantalla, una zona horaria y una lista de fuentes acota una población con rapidez. Los atributos correlacionados aportan menos, porque un agente de usuario que nombra un teléfono concreto ya implica una lista corta de números de núcleos plausibles.

Un valor raro acota aún más. Un informe de 24, 32 o 64 sugiere una estación de trabajo o un servidor, algo poco común entre las poblaciones de navegación cotidiana. El número no dice nada de una persona, pero puede hacer que una sesión sea más fácil de distinguir entre otras que informan 4 u 8.

Los dispositivos móviles añaden otra capa. Algunas familias de chips asocian un número concreto con otras propiedades, como la cadena de plataforma y la pantalla. Un teléfono que informa 8 junto con un patrón de agente de usuario concreto reduce el dispositivo a una lista corta de modelos, lo que da más información que cualquiera de los dos valores por separado. La misma lógica vale para los equipos de escritorio: un número común por sí solo resulta más distintivo cuando se combina con un valor de memoria o una plataforma poco habituales.

Los navegadores orientados a la privacidad resuelven este equilibrio de otra manera. Limitar o agrupar el número reduce la información que aporta, a costa de dar a las aplicaciones una pista menos exacta para dimensionar grupos de workers. Ambas opciones son legítimas. La pregunta útil para un desarrollador es cuánto depende la aplicación de un número exacto, y para quien revisa la privacidad, cuántas otras pistas de hardware se exponen junto a él.

Estos números son motivos de cautela, no un veredicto. Un navegador que informa un número común no queda protegido frente a otras señales, y uno que informa un número inusual no queda marcado automáticamente. El número es una entrada entre muchas, y su peso depende de lo que más pueda ver la página.

Cómo se contrasta un número de núcleos y por qué fallan los cambios manuales

La comparación comienza con navigator.hardwareConcurrency y puede incluir el valor devuelto por un Worker creado desde la misma página.

Además del número en sí, un sitio puede preguntarse si encaja. Tres tipos de coherencia importan cuando se informa un número de núcleos.

  1. Plausibilidad frente a otros valores. El número de núcleos debe tener sentido junto con la plataforma, el agente de usuario y la pista de memoria del dispositivo. Dos núcleos con 8 GB de memoria describen un portátil pequeño o una máquina virtual corrientes. Un número de 64 núcleos junto a un agente de usuario de teléfono describe un hardware que no existe.
  2. Coincidencia entre contextos. El hilo principal, los workers dedicados, los workers compartidos y los service workers deben informar un mismo valor. Un solo script de la página puede leerlos todos, así que una diferencia entre contextos se nota con facilidad.
  3. Coincidencia con el comportamiento paralelo. Un script podría ejecutar la misma tarea con un número creciente de Workers y observar dónde deja de mejorar el tiempo de finalización. Una máquina con 4 procesadores lógicos se estabiliza cerca de cuatro Workers, mientras que una con 16 sigue mejorando durante más tiempo.

Los pares que conviene revisar son concretos. Un agente de usuario de escritorio con Windows y 8 o 16 procesadores lógicos y 8 GB de memoria informada es habitual. Una plataforma Linux x86_64 con 4 núcleos apunta a hardware corriente de escritorio o servidor, mientras que una cadena de plataforma ARM con 8 núcleos sugiere un teléfono o un servidor ARM concreto. Ninguno de estos pares demuestra nada por sí solo; cada uno solo tiene que evitar contradecir el resto de la descripción.

La coincidencia entre contextos merece atención porque los workers se crean desde la página y comparten su origen, pero cada uno tiene su propio ámbito global. Un worker dedicado, un worker compartido que usan varias pestañas y un service worker que sobrevive a la página leen la propiedad de forma independiente, así que cualquier cambio hecho para la página debe aplicarse en cada uno de ellos y verificarse por separado.

La tercera comprobación es una posibilidad más que una medición rutinaria. Los navegadores reducen la precisión de los temporizadores, y las herramientas de medición con memoria compartida, como SharedArrayBuffer, exigen una página aislada entre orígenes con las cabeceras COOP y COEP. Esos requisitos limitan la precisión con la que una página puede medir el escalado, así que conviene tratar la comprobación como un motivo más para mantener alineados el número informado y el comportamiento real, no como un resultado seguro.

No se puede observar desde fuera cómo pondera un sitio concreto estas comprobaciones. Son objetivos de coherencia, no predicciones de lo que concluirá un sitio determinado.

Cuando el número de núcleos es erróneo, la discrepancia aparece en un orden fijo. El hilo principal es el primer lugar donde mirar, porque un script inyectado en la página suele cambiar solo ese ámbito. Un worker dedicado creado a partir de una URL de Blob va en segundo lugar, ya que ocupa tres líneas y expone una sobrescritura que nunca llegó a los ámbitos de worker. Un worker compartido y un service worker siguen después, porque necesitan más preparación pero también leen la propiedad desde su propio ámbito global. La memoria del dispositivo y la cadena de plataforma quedan al final, como comprobaciones de plausibilidad del número en el que coinciden los cuatro contextos.

Quien revisa puede recorrer ese orden como cuatro lecturas. Registra navigator.hardwareConcurrency en la página, luego self.navigator.hardwareConcurrency dentro de un worker dedicado, un worker compartido y un service worker, y compara los cuatro números. Lee la pista de memoria del dispositivo y la cadena de plataforma en la misma sesión para juzgar el número junto con el resto de la descripción. La ejecución de escalado descrita más abajo es una quinta pasada y necesita un host Linux o Windows.

Un resultado coincidente es anodino: los cuatro contextos devuelven 4, la memoria del dispositivo lee 8, la cadena de plataforma concuerda con el perfil y un benchmark deja de mejorar cerca de cuatro Workers. Un desajuste se lee con la misma facilidad. Una página que devuelve 4 mientras un worker dedicado devuelve 32 apunta a una sobrescritura en la página, no al hardware. Cuatro contextos coincidentes con un número que contradice la plataforma o la pista de memoria apuntan a un perfil editado de forma aislada, y contextos coincidentes que aun así escalan más allá del número declarado apuntan a la falta de afinidad de CPU en macOS.

Sobrescribir Navigator.prototype.hardwareConcurrency en una página no actualiza WorkerNavigator.hardwareConcurrency en el global separado de un worker.

La forma habitual de cambiar el valor es sobrescribir la propiedad desde una extensión o un script inyectado. Eso cambia el número que devuelve el hilo principal y deja tres huecos.

  • Desajuste de escalado. Si la propiedad dice 4 mientras la máquina tiene 16 núcleos, el trabajo paralelo termina antes de lo que permitiría una máquina de 4 núcleos. La afirmación y el comportamiento discrepan en cuanto alguien mide.
  • Huecos entre contextos. Una sobrescritura aplicada solo al objeto navigator de la página no llega a los ámbitos de worker salvo que también se inyecte allí, así que un worker puede seguir devolviendo el valor real.
  • Descripción parcial del dispositivo. Un número nuevo que no esté coordinado con el agente de usuario, la plataforma y la pista de memoria puede contradecirlos.

Los valores aleatorios empeoran el tercer hueco. Un número de 7 o 13 rara vez coincide con hardware que se vende. Incluso un valor común fijo como 8 contradice a un agente de usuario que nombra un teléfono cuyo chip tiene 4 núcleos. Una propiedad cambiada de forma aislada solo es tan fiable como el más débil de los demás valores que la rodean.

El hueco entre contextos es el más fácil de encontrar de los tres. Una página de prueba puede crear un worker dedicado a partir de una URL de Blob, devolver el valor y compararlo con el del hilo principal en pocas líneas. Un worker compartido y un service worker requieren algo más de preparación, pero la comparación es la misma, y cualquier diferencia es un defecto de la sobrescritura, no una propiedad del hardware.

El requisito es, por tanto, la coherencia: un único valor, informado en todos los contextos, que encaje con el resto de la descripción del dispositivo y concuerde con el modo en que escala el trabajo paralelo. Mantener esa coherencia a mano en todas esas superficies es la parte que resulta arriesgada.

Cómo comprobar un número de núcleos basado en un perfil

La primera comprobación registra navigator.hardwareConcurrency en el hilo principal y en cada Worker.

Una configuración basada en un perfil toma el número de un perfil de dispositivo completo y no de una sobrescritura manual. No existe una opción aparte para el número de núcleos, porque el valor procede del perfil. Cuatro comprobaciones muestran si el resultado es coherente.

  • Valor. Lee la propiedad y confirma que es igual al número de núcleos del perfil y no al del host.
  • Contextos. Léela dentro de un worker dedicado, un worker compartido y un service worker, y confirma que cada uno coincide con el hilo principal.
  • Plausibilidad. Confirma que el número es realista para la plataforma, el agente de usuario y la memoria del dispositivo del perfil.
  • Estabilidad. Léela en varias sesiones con el mismo perfil y confirma que nunca cambia.
ComprobaciónDónde se leeResultado esperado con un perfil de 4 núcleos
Valor del hilo principalnavigator.hardwareConcurrency en la página4, no el 32 del host
Valor en workerself.navigator.hardwareConcurrency en un worker dedicado4, idéntico al hilo principal
Worker compartido y service workerla misma propiedad en cada ámbito global4 en ambos, sin hueco entre contextos
Plausibilidadel número junto a las pistas de plataforma y memoria4 encaja con el perfil móvil, 32 no
const source = 'postMessage(self.navigator.hardwareConcurrency)';
const blob = new Blob([source], { type: 'text/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = event => console.log('worker:', event.data, 'main:', navigator.hardwareConcurrency);

Si una comprobación falla, las causas habituales son pocas.

  • El valor muestra el número de núcleos del host. El perfil no se cargó o no contiene el número de núcleos esperado. El valor procede del perfil, así que revisa primero el archivo del perfil.
  • Un trabajo paralelo sigue siendo rápido en macOS. macOS no expone la interfaz de afinidad de CPU que usan Linux y Windows, así que los Workers se ejecutan en todos los núcleos. El valor informado sigue procediendo del perfil, pero el tiempo paralelo puede no coincidir con él. Usa Linux o Windows cuando importe el comportamiento de escalado.
  • Los Workers parecen lentos. Es lo esperado cuando un perfil declara menos núcleos que el host, porque los Workers se limitan a propósito al número declarado.

El escalado es la quinta comprobación y depende de la plataforma. En un host con más núcleos de los que declara el perfil, un trabajo paralelo repartido entre más Workers que el número declarado debería dejar de acelerarse cerca de ese número. Cuando varias instancias del navegador comparten un host, cada instancia queda limitada de forma independiente por su propio perfil, de modo que un perfil de 4 núcleos y uno de 8 núcleos en el mismo servidor muestran curvas de escalado distintas que coinciden con lo que declara cada uno.

Un ejemplo práctico muestra el efecto. Un servidor de compilación de 32 núcleos ejecuta un perfil móvil que declara 4 núcleos. El perfil informa 4 en todos los contextos y los Workers quedan limitados a cuatro núcleos físicos, así que un benchmark paralelo con 8 Workers deja de acelerar cerca de cuatro Workers y muestra la curva de escalado de una máquina de 4 núcleos, no la que el host podría lograr. La velocidad absoluta sigue dependiendo del rendimiento por núcleo del host, que suele ser mayor que el de un teléfono.

El mismo host de 32 núcleos puede servir un perfil de 4 núcleos en varios contextos del navegador a la vez. Abre tres o cuatro páginas con el mismo perfil, lee navigator.hardwareConcurrency en cada página y abre un worker desde cada página para leer self.navigator.hardwareConcurrency allí. Cada página y cada worker deberían devolver 4, mientras que el host leído fuera del navegador controlado devuelve 32. Si un contexto devuelve 32, ese contexto se creó sin el perfil y no por el perfil, así que compara cómo se lanzó cada contexto antes de culpar al perfil.

Unos buenos hábitos mantienen el resultado predecible. Usa un perfil completo en lugar de sobrescribir una sola propiedad, elige un perfil cuyo número de núcleos encaje con su plataforma y su memoria, prueba siempre los contextos de worker y evita ajustar el número a mano. Si el número informado debe cambiar, cambia el perfil, porque los demás valores del dispositivo pertenecen a la misma descripción. Encontrarás más detalles en la guía de protección de las propiedades de navigator, la guía de la API de memoria del dispositivo, la guía de planificación de workers y el resumen sobre el fingerprinting del navegador.

Conserva un breve registro escrito de cada comprobación para poder comparar un cambio posterior: el perfil usado, la plataforma del host, el valor leído en el hilo principal y en cada tipo de worker, y el resultado de escalado en Linux o Windows. Si el registro nombra además la versión del navegador y la del perfil, es fácil saber si una diferencia posterior vino del perfil, del navegador o del host.

BotBrowser puede mantener coherente el navigator.hardwareConcurrency del perfil en el hilo principal y en los workers, y en Linux y Windows aplica afinidad de CPU a los hilos de Worker para que el escalado de los benchmarks paralelos coincida con el número de núcleos declarado. Eso ayuda cuando un host más grande ejecuta un perfil con menos núcleos, porque el número informado y el modo en que escala el trabajo paralelo siguen concordando. No puede añadir núcleos reales de CPU ni cambiar el hardware del host, no restringe los Workers mediante afinidad de CPU en macOS, donde el tiempo paralelo puede no coincidir con el perfil, y no garantiza cómo interpreta cada sitio la señal.

Conclusión práctica

Trata el número informado como una entrada de planificación acotada. Léelo en cada contexto de worker, compáralo con la plataforma del perfil y usa Linux o Windows cuando la afinidad de CPU forme parte de la prueba. Cambia el perfil en lugar de parchear una propiedad a mano.

Fuentes públicas

#Cpu#fingerprinting#hardwareConcurrency#privacidad

Este artículo forma parte de la guía: ¿Qué es la huella digital del navegador?

Lleva BotBrowser de la investigación a producción

Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.