Est-il possible d'écrire du code (ou un logiciel complet, plutôt qu'un morceau de code) qui ne fonctionnera pas correctement s'il est exécuté sur une CPU dont le nombre de cœurs est inférieur à N? Sans le vérifier explicitement et en échouant exprès:
SI (noOfCores <4) ALORS ne fonctionne pas correctement exprès
Je suis en train de regarder la configuration minimale requise pour un jeu ( Dragon Age: Inquisition ), qui indique au minimum un processeur à quatre cœurs. De nombreux joueurs disent qu'il ne fonctionne PAS sur les processeurs à deux cœurs et EVEN sur les processeurs Intel Core i3 avec deux cœurs physiques et deux cœurs logiques. Et ce n'est pas un problème de puissance de calcul.
D'après ce que j'ai compris, les threads sont complètement isolés du processeur par le système d'exploitation, ce qui est impossible.
Juste pour clarifier les choses:
Je ne demande pas "Puis-je connaître le nombre de cœurs de processeur à partir du code et échouer exprès?" ... Un tel code serait mal intentionné (vous oblige à acheter un processeur plus coûteux pour exécuter un programme - sans besoin de puissance de calcul). Je demande que votre code, par exemple, comporte quatre threads et échoue lorsque deux threads sont exécutés sur le même noyau physique (sans vérification explicite des informations système et échec intentionnel) .
En bref, peut-il y avoir un logiciel nécessitant plusieurs cœurs sans nécessiter de puissance de calcul supplémentaire provenant de plusieurs cœurs? Il faudrait simplement N cœurs physiques distincts.
la source
Réponses:
Cela peut être possible "par accident" avec une utilisation négligente de l’affinité principale. Considérons le pseudocode suivant:
Si vous démarrez quatre de ceux-ci sur un processeur à deux cœurs, le réglage de l'affinité de base pose problème, ou bien deux threads encombrent les cœurs disponibles et deux threads qui ne sont jamais planifiés. À aucun moment, il n'a explicitement demandé combien il y avait de cœurs.
(Si vous avez des threads de longue durée, la définition de l'affinité CPU améliore généralement le débit)
L'idée que les sociétés de jeux "obligent" les gens à acheter du matériel plus coûteux sans raison valable n'est pas très plausible. Cela ne peut que leur faire perdre des clients.
Edit: ce message a maintenant 33 votes positifs, ce qui est assez, car il est basé sur des suppositions éclairées!
Il semble que les gens aient DA: Je coure mal sur des systèmes dual-core: http://www.dsogaming.com/pc-performance-analyses/dragon-age-inquisition-pc-performance-analysis/ Cette analyse mentionne que la situation s'améliore grandement si l'hyperthreading est activé. Etant donné que HT n'ajoute plus d'unités d'émission d'instruction ni de cache, il permet simplement à un thread de s'exécuter pendant qu'un autre se trouve dans une stalle de cache, ce qui suggère fortement qu'il est lié uniquement au nombre de threads.
Une autre affiche affirme que la modification des pilotes graphiques fonctionne: http://answers.ea.com/t5/Dragon-Age-Inquisition/Working-solution-for-Intel-dual-core-CPUs/td-p/3994141 ; Etant donné que les pilotes graphiques ont tendance à être une ruche de misères et de voyous, ce n'est pas surprenant. Un ensemble notoire de pilotes avait un mode "correct & lent" par rapport au mode "rapide & incorrect" sélectionné si appelé depuis QUAKE.EXE. Il est tout à fait possible que les pilotes se comportent différemment pour différents nombres de processeurs apparents. Peut-être (retour à la spéculation) un mécanisme de synchronisation différent est utilisé. Mauvais usage des spinlocks ?
"L'utilisation abusive de primitives de verrouillage et de synchronisation" est une source très répandue de bogues. (Le bogue que je suis censé observer au travail en écrivant ceci est "un crash si vous modifiez les paramètres de l'imprimante en même temps que le travail d'impression est terminé").
Edit 2: les commentaires mentionnent le système d'exploitation essayant d'éviter la privation de threads. Notez que le jeu peut avoir son propre quasi-ordonnanceur interne pour attribuer le travail aux threads, et il y aura un mécanisme similaire dans la carte graphique elle-même (qui est en réalité un système multitâche à part entière). Les chances d'un bug dans l'un de ceux-ci ou l'interaction entre eux sont assez élevées.
www.ecsl.cs.sunysb.edu/tr/ashok.pdf (2008) est une thèse de troisième cycle sur l'amélioration de la planification des cartes graphiques, qui mentionne explicitement qu'elles utilisent normalement la planification selon le principe du premier arrivé, premier servi, qui est facile à mettre en œuvre. systèmes non préemptifs. La situation s'est-elle améliorée? Probablement pas.
la source
Il peut être nécessaire d’avoir 4 cœurs car l’application exécute quatre tâches dans des threads parallèles et s’attend à ce qu’elles se terminent presque simultanément.
Lorsque chaque thread est exécuté par un noyau séparé et que tous les threads ont exactement la même charge de travail de calcul, il est fort probable (mais loin d'être garanti) de terminer à peu près au même moment. Mais lorsque deux threads fonctionnent sur un même noyau, le timing sera beaucoup moins prévisible car le noyau changera de contexte tout le temps.
Les bogues qui surviennent à cause d'un timing de thread inattendu sont appelés " conditions de concurrence ".
Dans le contexte du développement de jeux, une architecture plausible avec ce type de problème pourrait être celle où différentes fonctionnalités du jeu sont simulées en temps réel par différents threads de la CPU. Lorsque chaque fonction est exécutée sur son propre noyau, elles sont toutes simulées à peu près à la même vitesse. Mais lorsque deux fonctionnalités fonctionnent sur un même noyau, elles ne seront simulées que deux fois moins vite que le reste du monde du jeu, ce qui pourrait entraîner toutes sortes de comportements étranges.
Notez qu'une architecture logicielle reposant sur des threads indépendants exécutés avec des timings spécifiques est extrêmement fragile et témoigne d'une très mauvaise compréhension de la programmation concurrente. Dans la quasi-totalité des API multithreading, certaines fonctionnalités permettent de synchroniser explicitement les threads afin d'éviter ce type de problèmes.
la source
Il est peu probable que ces "exigences minimales" représentent quelque chose en dessous duquel le jeu ne fonctionnera pas. Plus vraisemblablement, ils représentent quelque chose en dessous duquel le jeu ne fonctionnera pas avec des performances acceptables. Aucune entreprise de jeux vidéo ne souhaite traiter avec de nombreux clients se plaignant de performances médiocres lorsqu'ils les exécutent sur une seule boîte de 1 Ghz, même si le logiciel pouvait fonctionner techniquement. Donc, ils ont probablement délibérément conçu pour échouer dur sur les boîtes avec moins de cœurs que ce qui leur donnerait des performances acceptables.
Le taux de trame est une mesure importante dans les performances de jeu. Ils fonctionnent généralement à 30 ou 60 images par seconde. Cela signifie que le moteur de jeu doit rendre la vue actuelle à partir de l'état du jeu dans un laps de temps déterminé. Pour atteindre 60 ips, il ne lui reste plus que 16 ms. Les jeux avec des graphismes haut de gamme sont extrêmement gourmands en ressources CPU, ce qui représente un énorme compromis entre essayer d'améliorer la qualité (ce qui prend plus de temps) et la nécessité de respecter ce budget. Ainsi, le budget temps de chaque image est extrêmement limité.
En raison du manque de temps, le développeur souhaite idéalement disposer d'un accès exclusif à un ou plusieurs noyaux. Ils veulent aussi probablement pouvoir faire leur rendu dans un noyau, exclusivement, car c'est ce qui doit être fait avec ce budget temps, tandis que d'autres choses, telles que le calcul de l'état du monde, se déroulent selon un processus séparé où il n'en sera rien. intrusion.
En théorie, vous pourriez mettre tout cela sur un seul noyau, mais tout devient beaucoup plus difficile. Tout à coup, vous devez vous assurer que tout ce qui se passe dans l'état du jeu se passe assez rapidement et permet à votre rendu de se produire. Vous ne pouvez pas simplement en faire deux threads logiciels car il n’ya aucun moyen de faire comprendre au système d’exploitation "le thread A doit effectuer une quantité de travail X maximale en 16 ms, quel que soit le thread B".
Les développeurs de jeux n'ont aucun intérêt à vous faire acheter du nouveau matériel. La raison pour laquelle ils ont une configuration système requise est que le coût de la prise en charge des machines d'extrémité inférieure n'en vaut pas la peine.
la source
Trois threads en temps réel qui ne dorment jamais et un autre thread. S'il y a moins de quatre cœurs, le quatrième thread ne s'exécute jamais. Si le quatrième thread a besoin de communiquer avec l'un des threads temps réel pour que celui-ci se termine, le code ne se terminera pas avec moins de quatre cœurs.
Évidemment, si les threads temps réel attendent quelque chose qui ne leur permet pas de dormir (comme un verrou tournant), le concepteur du programme a tout gâché.
la source
Tout d'abord, les threads logiciels n'ont rien à voir avec les threads matériels et sont souvent mélangés. Les threads logiciels sont des morceaux de code pouvant être envoyés et exécutés séparément dans le contexte du processus. Les threads matériels sont principalement gérés par le système d'exploitation et sont envoyés au noyau du processeur lorsqu'il parle de programmes standard. Ces threads matériels sont distribués en fonction de la charge; le répartiteur de threads matériels agit plus ou moins comme un équilibreur de charge.
Toutefois, lorsqu'il s'agit de jeux, en particulier de jeux haut de gamme, les threads matériels sont parfois gérés par le jeu lui-même ou le jeu indique au répartiteur de threads quoi faire. En effet, chaque tâche ou groupe de tâches n'a pas la même priorité que dans un programme normal. Étant donné que Dragon Age est issu d’un studio de jeu haut de gamme utilisant des moteurs de jeu haut de gamme, je peux imaginer qu’il utilise la répartition "manuelle" et que le nombre de cœurs devient alors une exigence système minimale. N'importe quel programme se bloquerait lorsque j'enverrais une partie de code au 3ème cœur physique s'exécutant sur une machine avec seulement 1 ou 2 cœurs.
la source
Puisqu'il est possible d'utiliser la virtualisation pour avoir plus de cœurs virtuels que physiques et que le logiciel ne sache pas qu'il s'exécute sur une virtualisation et pense au contraire qu'il possède autant de cœurs physiques, je dirais qu'un tel logiciel n'est pas possible.
C'est-à-dire qu'il n'est pas possible d'écrire un logiciel qui s'arrête toujours sur moins de N cœurs.
Comme d'autres l'ont fait remarquer, certaines solutions logicielles peuvent potentiellement vérifier, en particulier si le système d'exploitation et le code utilisés offrent peu de protection contre les conditions de concurrence critique lorsque N processus s'exécutent sur <N processeurs. Le vrai truc, c'est que le code échouera si vous avez moins de N processeurs, mais pas lorsque vous avez N processeurs mais que votre système d'exploitation peut affecter le travail à moins de N processeurs.
la source
Il se peut que trois threads agissent (générant des arrière-plans ou des mouvements de NPC) et transmettant des événements à un quatrième, censé agréger / filtrer les événements et mettre à jour le modèle de vue. Si le quatrième thread ne reçoit pas tous les événements (car il n'est pas planifié sur un core), le modèle de vue ne sera pas mis à jour correctement. Cela peut ne se produire que sporadiquement, mais ces cœurs doivent être disponibles à tout moment. Cela pourrait expliquer pourquoi vous ne voyez pas toujours une utilisation élevée du processeur, mais que le jeu ne fonctionne toujours pas correctement.
la source
Je pense que Joshua se dirige dans la bonne voie, mais pas à sa conclusion.
Supposons que vous ayez une architecture dans laquelle trois threads sont écrits pour faire tout ce qu'ils peuvent: quand ils terminent ce qu'ils font, ils recommencent. Pour maintenir les performances, ces threads ne libèrent pas le contrôle, ils ne veulent pas risquer le décalage du planificateur de tâches Windows. Tant qu'il y a 4 noyaux ou plus, cela fonctionne très bien, sinon, il échoue.
En général, ce serait une mauvaise programmation, mais les jeux sont un autre problème - lorsque vous devez choisir entre une conception inférieure sur tout le matériel ou une conception supérieure sur un matériel suffisamment correct ou une défaillance sur un matériel inférieur, les développeurs de jeux choisissent généralement exiger le matériel.
la source
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?
Absolument. L'utilisation de threads en temps réel serait un bon exemple d'une situation dans laquelle cela est non seulement possible, mais également la manière souhaitée (et souvent la seule manière correcte) de mener à bien la tâche. Cependant, les threads en temps réel sont généralement limités au noyau du système d'exploitation, généralement pour les pilotes qui doivent pouvoir garantir qu'un événement matériel est géré dans un laps de temps défini. Vous ne devriez pas avoir de threads en temps réel dans les applications utilisateur normales et je ne suis pas sûr qu'il soit même possible d'en avoir un dans une application en mode utilisateur Windows. Généralement, les systèmes d'exploitation rendent intentionnellement impossible d'effectuer cette opération depuis un utilisateur, précisément parce que cela permet à une application donnée de prendre le contrôle du système.
Concernant les applications utilisateur: Votre hypothèse selon laquelle la vérification d'un nombre donné de threads afin de l'exécuter est nécessairement malveillante dans l'intention n'est pas correcte. Par exemple, vous pouvez avoir 2 tâches de longue durée et à forte intensité de performances qui nécessitent un noyau pour elles-mêmes. Quelle que soit la vitesse du cœur du processeur, le partage d’un cœur avec d’autres threads peut entraîner une dégradation grave et inacceptable des performances en raison de la destruction des caches de cache et des pénalités normales encourues en cas de commutation de threads (assez conséquents). Dans ce cas, cela serait parfaitement raisonnable, spécialement pour un jeu, définissez chacun de ces threads de manière à avoir une affinité uniquement sur un noyau pour chacun d’eux, puis définissez tous vos autres threads de manière à ne pas avoir d’affinité sur ces 2 noyaux. Pour ce faire, cependant, vous '
la source
Tout code utilisant des spinlocks avec une quantité perceptible de conflits de verrous fonctionnera terriblement (dans une mesure où, pour une application comme un jeu, vous pouvez dire "ne fonctionne pas" ) si le nombre de threads dépasse le nombre de cœurs.
Imaginons par exemple un thread producteur qui soumet des tâches à une file qui dessert 4 threads grand public. Il n'y a que deux noyaux:
Le producteur essaie d’obtenir le spinlock, mais celui-ci est détenu par un consommateur utilisant l’autre noyau. Les deux cœurs fonctionnent en même temps que le producteur tourne, attendant que le verrou soit libéré. C'est déjà mauvais, mais pas aussi grave que cela va l'être.
Malheureusement, le thread consommateur est à la fin de son temps, il est donc préempté et un autre thread consommateur est planifié. Il essaie de mettre la main sur le verrou, mais bien sûr, le verrou est pris. À présent, deux cœurs tournent et attendent quelque chose d’inévitable.
Le fil producteur arrive à la fin de sa tranche de temps et est préempté, un autre consommateur se réveille. Encore une fois, deux consommateurs attendent la libération d'un verrou, et cela ne se produira tout simplement pas avant que deux délais supplémentaires soient écoulés.
[...] Enfin, le consommateur qui tenait le spinlock a relâché le verrou. Il est immédiatement pris par celui qui tourne sur l’autre noyau. Il y a 75% de chances (3 pour 1) que ce soit un autre fil de consommation. En d'autres termes, il est probable à 75% que le producteur soit toujours bloqué. Bien entendu, cela signifie également que les consommateurs stagnent. Sans les tâches médiocres du producteur, ils n'ont rien à faire.
Notez que cela fonctionne en principe avec n'importe quel type de verrouillage, pas seulement les spinlocks - mais l'effet dévastateur est beaucoup plus évident avec les spinlocks car le processeur continue de graver des cycles sans réaliser rien.
Maintenant, imaginez qu’en plus de ce qui précède, certains programmeurs eurent l’idée géniale d’utiliser un thread dédié avec une affinité définie sur le premier noyau. RDTSC donnera donc des résultats fiables sur tous les processeurs (ce n’est pas le cas, mais certains le pensent).
la source
Si je comprends ce que vous demandez, c'est possible, mais c'est une très, très mauvaise chose.
L'exemple canonique de ce que vous décrivez consisterait à maintenir un compteur incrémenté de plusieurs threads. Cela ne nécessite presque rien en termes de puissance de calcul mais nécessite une coordination minutieuse entre les threads. Tant qu'un seul thread à la fois effectue un incrément (qui est en fait une lecture suivie d'un ajout suivi d'une écriture), sa valeur sera toujours correcte. En effet, un thread lit toujours la valeur "précédente" correcte, en ajoute une et écrit la valeur "suivante" correcte. Obtenez deux threads dans l'action en même temps et les deux liront la même valeur "précédente", obtiendront le même résultat de l'incrément et écrivent la même valeur "suivante". Le compteur n'aura effectivement été incrémenté qu'une seule fois, même si deux threads pensent l'avoir fait.
Cette dépendance entre timing et exactitude est ce que l’informatique appelle une situation de concurrence critique .
Les conditions de concurrence sont souvent évitées en utilisant des mécanismes de synchronisation pour s'assurer que les threads souhaitant opérer sur une donnée partagée doivent se mettre en ligne pour accéder. Le compteur décrit ci-dessus peut utiliser un verrou en lecture-écriture pour cela.
Sans accès à la conception interne de Dragon Age: Inquisition , tout ce que l’on peut faire est de spéculer sur les raisons de son comportement. Mais je vais essayer quelques choses que j'ai vu faire dans ma propre expérience:
Il se peut que le programme soit basé sur quatre threads qui ont été ajustés pour que tout fonctionne lorsque les threads fonctionnent presque sans interruption sur leurs propres cœurs physiques. Le "réglage" peut prendre la forme de réarrangement de code ou d'insertion de couches de sommeil dans des endroits stratégiques afin d'atténuer les bugs induits par la race qui apparaissent lors du développement. Encore une fois, tout cela est une conjecture, mais j’ai vu les conditions de course «résolues» de cette façon plus de fois que je n’aimerais compter.
L'exécution d'un programme comme celui-ci sur un environnement moins capable que l'environnement pour lequel il a été configuré introduit des modifications de minutage résultant du code ne s'exécutant pas aussi rapidement ou, plus probablement, de changements de contexte. Les commutations de contexte se produisent de manière physique (les cœurs physiques de la CPU changent entre les tâches que ses cœurs logiques conservent) et logique (le système d’exploitation de la CPU attribue du travail aux cœurs), mais l’un ou l’autre de ces écarts serait le délai d'exécution "prévu". Cela peut faire ressortir un mauvais comportement.
Si Dragon Age: Inquisition ne prend pas la simple décision de s'assurer qu'il y a suffisamment de cœurs physiques disponibles avant de poursuivre, c'est la faute de EA. Ils dépensent probablement une petite fortune pour répondre aux appels de support et aux courriers électroniques de personnes qui ont essayé de lancer le jeu avec trop peu de matériel.
la source
Windows a des fonctionnalités intégrées pour cela: la fonction GetLogicalProcessorInformation est dans l' API Windows . Vous pouvez l'appeler depuis votre programme pour obtenir des informations sur les cœurs, les cœurs virtuels et l'hyperthreading.
Donc, la réponse à votre question serait: oui.
la source
/proc/cpuinfo
etsysconf(_SC_NPROCESSORS_ONLN)
(ce dernier étant mentionné dans POSIX). L'utilisation de l'info pour imposer un seuil de performance minimum reste néanmoins une forme assez mauvaise.