¿Es posible escribir código (o software completo, en lugar de un fragmento de código) que no funcione correctamente cuando se ejecuta en una CPU que tiene menos de N núcleos? Sin verificarlo explícitamente y fallar a propósito:
SI (noOfCores <4) ENTONCES no funciona correctamente a propósito
Estoy viendo los requisitos mínimos del sistema de un juego ( Dragon Age: Inquisition ), y establece un mínimo de una CPU de cuatro núcleos. Muchos jugadores dicen que NO se ejecuta en CPU de dos núcleos y INCLUSO en Intel Core i3 con dos núcleos físicos y dos lógicos. Y NO es un problema de potencia informática.
Según tengo entendido, los hilos están completamente aislados de la CPU por el sistema operativo ya que eso no se puede hacer.
Solo para aclarar las cosas:
NO estoy preguntando "¿Puedo averiguar el número de núcleos de CPU del código y fallar a propósito?" ... Tal código sería mal intencionado (lo obliga a comprar una CPU más costosa para ejecutar un programa, sin la necesidad de potencia computacional). Le pido que su código, por ejemplo, tenga cuatro hilos y falle cuando dos hilos se ejecutan en el mismo núcleo físico (sin verificar explícitamente la información del sistema y fallar a propósito) .
En resumen, ¿puede haber un software que requiera múltiples núcleos, sin necesidad de potencia informática adicional que provenga de múltiples núcleos? Solo requeriría N núcleos físicos separados.
fuente
Respuestas:
Es posible hacer esto "por accidente" con el uso descuidado de la afinidad central. Considere el siguiente pseudocódigo:
Si inicia cuatro de ellas en una CPU de dos núcleos, entonces algo falla con la configuración de afinidad central o termina con dos hilos que acaparan los núcleos disponibles y dos hilos que nunca se programan. En ningún momento ha preguntado explícitamente cuántos núcleos hay en total.
(Si tiene subprocesos de larga ejecución, la configuración de la afinidad de la CPU generalmente mejora el rendimiento)
La idea de que las compañías de juegos están "obligando" a las personas a comprar hardware más caro sin una buena razón no es muy plausible. Solo puede perderlos clientes.
Editar: esta publicación ahora tiene 33 votos a favor, ¡lo cual es bastante dado que se basa en conjeturas educadas!
Parece que la gente tiene DA: I para ejecutar, mal, en sistemas de doble núcleo: http://www.dsogaming.com/pc-performance-analyses/dragon-age-inquisition-pc-performance-analysis/ Ese análisis menciona que la situación mejora enormemente si se activa hyperthreading. Dado que HT no agrega más unidades de problema de instrucción o caché, simplemente permite que se ejecute un subproceso mientras otro está en una parada de caché, lo que sugiere fuertemente que está vinculado a la cantidad pura de subprocesos.
Otro póster afirma que cambiar los controladores de gráficos funciona: http://answers.ea.com/t5/Dragon-Age-Inquisition/Working-solution-for-Intel-dual-core-CPUs/td-p/3994141 ; Dado que los controladores gráficos tienden a ser una miserable colmena de escoria y villanía, esto no es sorprendente. Un conjunto notorio de controladores tenía un modo "correcto y lento" versus "rápido e incorrecto" que se seleccionaba si se llamaba desde QUAKE.EXE. Es completamente posible que los controladores se comporten de manera diferente para diferentes números de CPU aparentes. Quizás (volviendo a la especulación) se usa un mecanismo de sincronización diferente. ¿Mal uso de spinlocks ?
El "mal uso de las primitivas de bloqueo y sincronización" es una fuente muy común de errores. (El error que se supone que estoy mirando en el trabajo mientras escribo esto es "bloqueo si se cambia la configuración de la impresora al mismo tiempo que finaliza el trabajo de impresión").
Edición 2: los comentarios mencionan que el sistema operativo intenta evitar el hambre de hilos. Tenga en cuenta que el juego puede tener su propio cuasi-programador interno para asignar trabajo a subprocesos, y habrá un mecanismo similar en la propia tarjeta gráfica (que es efectivamente un sistema multitarea propio). Las posibilidades de un error en uno de esos o la interacción entre ellos son bastante altas.
www.ecsl.cs.sunysb.edu/tr/ashok.pdf (2008) es una tesis de posgrado sobre una mejor programación para tarjetas gráficas que menciona explícitamente que normalmente utilizan la programación por orden de llegada, que es fácil de implementar en sistemas no preventivos. ¿Ha mejorado la situación? Probablemente no.
fuente
Podría ser necesario tener 4 núcleos porque la aplicación ejecuta cuatro tareas en subprocesos paralelos y espera que finalicen casi simultáneamente.
Cuando cada subproceso es ejecutado por un núcleo separado y todos los subprocesos tienen exactamente la misma carga de trabajo computacional, es muy probable (pero lejos de estar garantizado) que terminen aproximadamente al mismo tiempo. Pero cuando dos hilos se ejecutan en un núcleo, el tiempo será mucho menos predecible porque el núcleo cambiará el contexto entre los dos hilos todo el tiempo.
Los errores que se producen debido a la sincronización inesperada del subproceso se denominan " condiciones de carrera ".
En el contexto del desarrollo del juego, una arquitectura plausible con este tipo de problema podría ser una en la que diferentes características del juego se simulan en tiempo real por diferentes hilos de CPU. Cuando cada característica se ejecuta en un núcleo propio, todas se simulan con aproximadamente la misma velocidad. Pero cuando dos características se ejecutan en un núcleo, ambas solo se simularán la mitad de rápido que el resto del mundo del juego, lo que podría causar todo tipo de comportamientos extraños.
Tenga en cuenta que una arquitectura de software que depende de subprocesos independientes que se ejecutan con tiempos específicos es extremadamente frágil y una señal de muy mala comprensión de la programación concurrente. Hay características disponibles en prácticamente todas las API de subprocesos múltiples para sincronizar subprocesos explícitamente para evitar este tipo de problemas.
fuente
Es poco probable que estos "requisitos mínimos" representen algo por debajo del cual el juego no se ejecutará. Mucho más probable es que representen algo por debajo del cual el juego no se ejecutará con un rendimiento aceptable. Ninguna compañía de juegos quiere lidiar con muchos clientes quejándose del mal desempeño cuando lo están ejecutando en un solo núcleo de 1 Ghz, incluso si el software podría ejecutarse técnicamente. Por lo tanto, probablemente diseñen deliberadamente para fallar con fuerza en cajas con menos núcleos de los que les darían un rendimiento aceptable.
Una métrica importante en el rendimiento del juego es la velocidad de fotogramas. Por lo general, se ejecutan a 30 o 60 cuadros por segundo. Esto significa que el motor del juego tiene que representar la vista actual desde el estado del juego en un período de tiempo fijo. Para lograr 60 fps, tiene un poco más de 16 ms para hacer esto. Los juegos con gráficos de alta gama están extremadamente vinculados a la CPU, por lo que hay una gran toma y daca entre tratar de impulsar una mayor calidad (que lleva más tiempo) y la necesidad de mantenerse en este presupuesto de tiempo. Por lo tanto, el presupuesto de tiempo para cada cuadro es extremadamente ajustado.
Debido a que el presupuesto de tiempo es limitado, el desarrollador idealmente quiere acceso exclusivo a uno o más núcleos. También es probable que quieran poder hacer sus tareas de renderizado en un núcleo, exclusivamente, ya que es lo que debe hacerse con ese presupuesto de tiempo, mientras que otras cosas, como calcular el estado mundial, suceden en un proceso separado donde no entrometerse.
En teoría, podría agrupar todo esto en un solo núcleo, pero luego todo se vuelve mucho más difícil. De repente, debes asegurarte de que todo lo relacionado con el estado del juego suceda lo suficientemente rápido y permita que tu renderizado suceda. No puede simplemente convertirlos en dos subprocesos de software porque no hay forma de hacer que el sistema operativo entienda que "el subproceso A debe completar una cantidad X de trabajo en 16 ms, independientemente de lo que haga el subproceso B".
Los desarrolladores de juegos no tienen ningún interés en hacerte comprar nuevo hardware. La razón por la que tienen requisitos del sistema es que el costo de soportar máquinas de gama baja no vale la pena.
fuente
Tres hilos en tiempo real que nunca duermen y otro hilo. Si hay menos de cuatro núcleos, el cuarto subproceso nunca se ejecuta. Si el cuarto subproceso necesita comunicarse con uno de los subprocesos en tiempo real para que el subproceso en tiempo real finalice, el código no terminará con menos de cuatro núcleos.
Obviamente, si los subprocesos en tiempo real están esperando algo que no les permite dormir (como un spinlock), el diseñador del programa se equivocó.
fuente
En primer lugar, los hilos de software no tienen nada que ver con los hilos de hardware y a menudo se mezclan. Los subprocesos de software son piezas de código que pueden enviarse y ejecutarse por sí mismas dentro del contexto del proceso. Los subprocesos de hardware son administrados principalmente por el sistema operativo y se envían al núcleo del procesador cuando se habla de programas regulares. Estos subprocesos de hardware se envían en función de la carga; El despachador de hilos de hardware actúa más o menos como un equilibrador de carga.
Sin embargo, cuando se trata de juegos, especialmente juegos de alta gama, a veces los hilos de hardware son administrados por el juego en sí o el juego le indica al despachador de hilos de hardware qué hacer. Esto se debe a que cada tarea o grupo de tareas no tiene la misma prioridad que en un programa normal. Debido a que Dragon Age proviene de un estudio de juegos de alta gama que usa motores de juegos de alta gama, puedo imaginar que usa un despacho "manual" y luego el número de núcleos se convierte en un requisito mínimo del sistema. Cualquier programa se bloqueará cuando envíe un fragmento de código al tercer núcleo físico que se ejecuta en una máquina con solo 1 o 2 núcleos.
fuente
Dado que es posible usar virtualizar para tener más núcleos virtuales que físicos y el software no sabría que se está ejecutando en un virtualizar y, en cambio, pensar que tiene tantos núcleos físicos, diría que dicho software no es posible.
Es decir, no es posible escribir software que siempre se detendrá en menos de N núcleos.
Como otros han señalado, existen soluciones de software que pueden verificar potencialmente, especialmente si el sistema operativo y el código que se usa tienen poca protección contra las condiciones de carrera cuando N procesos se ejecutan en procesadores <N. El verdadero truco es el código que fallará cuando tenga menos de N procesadores, pero no fallará cuando tenga N procesadores pero tenga un sistema operativo que pueda asignar trabajo a menos de N procesadores.
fuente
Podría ser que hay tres hilos haciendo algo (generando fondos o generando movimiento NPC) y pasando eventos a un cuarto, que se supone que agrega / filtra los eventos y actualiza el modelo de vista. Si el cuarto hilo no recibe todos los eventos (porque no está programado en un núcleo), entonces el modelo de vista no se actualiza correctamente. Esto solo puede suceder esporádicamente, pero esos núcleos deben estar disponibles en cualquier momento. Esto podría explicar por qué no ves un uso elevado de la CPU todo el tiempo, pero el juego no funciona correctamente de todos modos.
fuente
Creo que Joshua se dirige por el camino correcto, pero no hasta su conclusión.
Suponga que tiene una arquitectura en la que hay tres subprocesos escritos para hacer todo lo posible; cuando terminan lo que están haciendo, lo vuelven a hacer. Para mantener el rendimiento, estos subprocesos no liberan el control de nada, no quieren arriesgarse al retraso del programador de tareas de Windows. Mientras haya 4 o más núcleos, esto funciona bien, fallará si no los hay.
En general, esto sería una mala programación, pero los juegos son otra cuestión: cuando se enfrenta a una elección entre un diseño que es inferior en todo el hardware o un diseño que es superior en un hardware suficientemente bueno o una falla en los desarrolladores de juegos de hardware inferiores, generalmente eligen para requerir el hardware.
fuente
Is it possible to write code (or complete software, rather than a piece of code) that won't work properly when run on a CPU that has less than N number of cores?
Absolutamente. El uso de subprocesos en tiempo real sería un buen ejemplo de una situación en la que esto no solo es posible, sino la forma deseada (y a menudo, la única forma correcta) de hacer el trabajo. Sin embargo, los subprocesos en tiempo real generalmente se limitan al núcleo del sistema operativo, generalmente para los controladores que deben poder garantizar que un evento de hardware de algún tipo se maneje dentro de un período de tiempo definido. No debe tener subprocesos en tiempo real en aplicaciones de usuario normales y no estoy seguro de que sea posible tener uno en una aplicación de modo de usuario de Windows. En general, los sistemas operativos hacen que sea intencionalmente imposible hacerlo desde la tierra del usuario precisamente porque permite que una aplicación determinada tome el control del sistema.
Con respecto a las aplicaciones de aterrizaje de usuarios: su suposición de que verificar una determinada cantidad de subprocesos para ejecutarse es necesariamente maliciosa en su intención no es correcta. Por ejemplo, podría tener 2 tareas de ejecución prolongada e intensivas en rendimiento que necesitan un núcleo para sí mismas. Independientemente de la velocidad del núcleo de la CPU, compartir un núcleo con otros subprocesos podría ser una degradación del rendimiento grave e inaceptable debido a la alteración del caché junto con las penalizaciones normales incurridas por el cambio de subprocesos (que son bastante sustanciales). En este caso, sería perfectamente razonable, especialmente para un juego, configurar cada uno de estos hilos para que tenga afinidad solo en un núcleo particular para cada uno de ellos y luego configurar todos sus otros hilos para que no tengan afinidad en esos 2 núcleos. Sin embargo, para hacer esto, usted '
fuente
Cualquier código que use spinlocks con cualquier cantidad notable de contención de bloqueo funcionará terriblemente (hasta un punto donde, para una aplicación como un juego, puede decir "no funciona" ) si el número de hilos excede el número de núcleos.
Imagine, por ejemplo, un hilo productor que envía tareas a una cola que sirve a 4 hilos consumidores. Solo hay dos núcleos:
El productor intenta obtener el spinlock, pero lo tiene un consumidor que se ejecuta en el otro núcleo. Los dos núcleos se ejecutan de manera sincronizada mientras el productor gira, esperando que se libere el bloqueo. Esto ya es malo, pero no tan malo como podría ser.
Desafortunadamente, el hilo del consumidor está al final de su tiempo cuántico, por lo que se adelanta y se programa otro hilo del consumidor. Intenta apoderarse de la cerradura, pero, por supuesto, la cerradura se toma, por lo que ahora dos núcleos giran y esperan algo que no puede suceder.
El subproceso productor llega al final de su intervalo de tiempo y se adelanta, otro consumidor se despierta. Una vez más, dos consumidores están esperando que se libere un bloqueo, y simplemente no sucederá antes de que pasen dos cuánticos más.
[...] Finalmente, el consumidor que sostenía el spinlock ha liberado el bloqueo. Inmediatamente lo toma quien está girando sobre el otro núcleo. Hay un 75% de posibilidades (3 a 1) de que sea otro hilo de consumo. En otras palabras, es 75% probable que el productor todavía esté estancado. Por supuesto, esto significa que los consumidores también se estancan. Sin las tareas de sustitución del productor, no tienen nada que hacer.
Tenga en cuenta que esto funciona en principio con cualquier tipo de bloqueo, no solo spinlocks, sino que el efecto devastador es mucho más destacado con los spinlocks porque la CPU mantiene los ciclos de grabación mientras no logra nada.
Ahora imagine que, además de lo anterior, algún programador tuvo la brillante idea de utilizar un hilo dedicado con afinidad establecida en el primer núcleo, por lo que RDTSC dará resultados confiables en todos los procesadores (de todos modos, no lo hará, pero algunas personas piensan que sí).
fuente
Si entiendo lo que estás preguntando, es posible, pero es algo muy, muy malo.
El ejemplo canónico de lo que está describiendo sería mantener un contador que se incrementa en múltiples hilos. Esto no requiere casi nada en términos de potencia informática, pero requiere una coordinación cuidadosa entre los hilos. Mientras solo un hilo a la vez haga un incremento (que en realidad es una lectura seguida de una adición seguida de una escritura), su valor siempre será correcto. Esto se debe a que un subproceso siempre leerá el valor "anterior" correcto, agregará uno y escribirá el valor "siguiente" correcto. Obtenga dos hilos en la acción al mismo tiempo y ambos leerán el mismo valor "anterior", obtendrán el mismo resultado del incremento y escribirán el mismo valor "siguiente". El contador se habrá incrementado efectivamente solo una vez, aunque dos hilos piensen que cada uno lo hizo.
Esta dependencia entre el tiempo y la corrección es lo que la informática llama una condición de carrera .
Las condiciones de carrera a menudo se evitan mediante el uso de mecanismos de sincronización para asegurarse de que los subprocesos que desean operar en una parte de datos compartidos tengan que estar en línea para acceder. El contador descrito anteriormente podría usar un bloqueo de lectura-escritura para esto.
Sin acceso al diseño interno de Dragon Age: Inquisition , todo lo que cualquiera puede hacer es especular sobre por qué se comporta de la manera en que lo hace. Pero intentaré basarme en algunas cosas que he visto en mi propia experiencia:
Puede ser que el programa se base en cuatro hilos que se han ajustado para que todo funcione cuando los hilos se ejecutan sin interrupción en sus propios núcleos físicos. El "ajuste" podría venir en la forma de reorganizar el código o insertar durmientes en lugares estratégicos para mitigar los errores inducidos por la condición de la raza que surgieron durante el desarrollo. Nuevamente, todo esto es una conjetura, pero he visto las condiciones de carrera "resueltas" de esa manera más veces de las que me gustaría contar.
Ejecutar un programa como ese en cualquier cosa menos capaz que el entorno para el que se ajustó introduce cambios de tiempo que son el resultado de que el código no se ejecuta tan rápido o, más probablemente, cambios de contexto. Los cambios de contexto ocurren de manera física (es decir, los núcleos físicos de la CPU cambian entre el trabajo que tienen sus núcleos lógicos) y lógico (es decir, el sistema operativo en la CPU está asignando trabajo a los núcleos), pero tampoco es una divergencia significativa de lo que sería el tiempo de ejecución "esperado". Eso puede provocar un mal comportamiento.
Si Dragon Age: Inquisition no da el simple paso de asegurarse de que haya suficientes núcleos físicos disponibles antes de continuar, es culpa de EA. Probablemente estén gastando una pequeña fortuna respondiendo llamadas de soporte y correos electrónicos de personas que intentaron ejecutar el juego con muy poco hardware.
fuente
Windows tiene una funcionalidad incorporada para esto: la función GetLogicalProcessorInformation está en la API de Windows . Puede llamarlo desde su programa para obtener información sobre núcleos, núcleos virtuales e hyperthreading.
Entonces la respuesta a su pregunta sería: Sí.
fuente
/proc/cpuinfo
ysysconf(_SC_NPROCESSORS_ONLN)
(este último se menciona en POSIX). Sin embargo, usar la información para imponer un umbral de rendimiento mínimo sigue siendo una forma bastante mala.