2015-11-21 1 views
2

J'ai écrit un programme multi-thread pour démontrer l'effet hors service du processeur Intel. Le programme est joint à la fin de ce post. Le résultat attendu doit être celui où x est imprimé 42 ou 0 par le gestionnaire1. Cependant, le résultat actuel est toujours 42, ce qui signifie que l'effet hors service ne se produit pas.Programme de test pour l'effet hors service du processeur

J'ai compilé le programme avec la commande "gcc -pthread -O0 out-of-order-test.c" Je lance le programme compilé sur Ubuntu 12.04 LTS (noyau Linux 3.8.0-29-generic) sur Intel Processeur IvyBridge Intel (R) Xeon (R) CPU E5-1650 v2.

Est-ce que quelqu'un sait ce que je devrais faire pour voir l'effet hors service?

#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 

int f = 0, x = 0; 

void* handler1(void *data) 
{ 
    while (f == 0); 
    // Memory fence required here 
    printf("%d\n", x); 
} 

void* handler2(void *data) 
{ 
    x = 42; 
    // Memory fence required here 
    f = 1; 
} 

int main(int argc, char argv[]) 
{ 
    pthread_t tid1, tid2; 

    pthread_create(&tid1, NULL, handler1, NULL); 
    pthread_create(&tid2, NULL, handler2, NULL); 

    sleep(1); 
    return 0; 
} 
+2

Ce n'est pas à propos de hors-service, mais une condition de course. (x86 est une architecture de chargement/stockage en ordre, btw.). – Olaf

+0

@Olaf, Merci pour votre commentaire. Cependant, x86 a au moins un problème de chargement après magasin selon http://stackoverflow.com/questions/7346893/out-of-order-execution-and-memory-fences. S'il a la dépendance de données sur un noyau, je sais que le matériel gardera la commande. Sinon, le mécanisme hors service peut exécuter l'instruction suivante avant l'instruction précédente. – Mike

+1

hors service ici ne concerne pas l'exécution d'instruction de toute façon.Très peu disponible pour une architecture multi-enjeux comme (presque) toutes les architectures haut de gamme. C'est à propos de charge/magasin. Mais vous n'exploiterez rien ici avec votre coe. Et probablement pas avec n'importe quel code C - au moins pas fiable. Ce dernier simplement parce que vous avez besoin d'une séquence d'instructions spécifique, dont vous n'aurez pas le contrôle lors de l'utilisation d'un compilateur. Alors, plongez dans l'assembleur et essayez. Bonne chance. – Olaf

Répondre

3

S'IL VOUS PLAÎT NOTE: Les adresses suivantes seulement MEMORY réorganisation. À ma connaissance, vous ne pouvez pas observer une exécution hors-ligne en dehors du pipeline, car cela constituerait un échec du CPU à adhérer à son interface. (par exemple: vous devriez le dire à Intel, ce serait un bug). Plus précisément, il devrait y avoir un échec dans le tampon de réapprovisionnement et la tenue de livres de retraite d'instruction.

Selon Intel's documentation (spécifiquement Volume 3A, section 8.2.3.4):

Le modèle mémoire Intel commande 64 permet à une charge d'être commandé avec un magasin plus tôt à un emplacement différent.

Il précise également (je résume, mais tout cela est disponible dans la section 8.2 Commande de mémoire avec des exemples en 8.2.3) que les charges ne sont jamais réorganisés avec des charges, les magasins ne sont jamais réorganisés avec les magasins et les magasins et jamais réordonné avec des charges plus tôt. Cela signifie qu'il y a clôtures implicites entre ces opérations dans Intel 64.

Pour observer le réordonnancement de la mémoire, il vous suffit d'implémenter cet exemple avec suffisamment de soin pour en observer les effets. Here is a link à une mise en œuvre complète que j'ai fait qui le démontre. (Je ferai un suivi avec plus de détails dans le message d'accompagnement here).

Essentiellement le premier fil (processor_0 de l'exemple) ce que cela:

x = 1; 
#if CPU_FENCE 
    __cpu_fence(); 
#endif 
    r1 = y; 

à l'intérieur d'une boucle while dans son propre fil (épinglé à une unité centrale de traitement à l'aide SCHED_FIFO:99).

Le second (observateur, dans ma démonstration) Est-ce:

y = 1; 
#if CPU_FENCE 
    __cpu_fence(); 
#endif 
    r2 = x; 

également dans une boucle while dans son propre thread avec les mêmes paramètres du planificateur.

Réorganise sont vérifiées comme celui-ci (exactement comme dans l'exemple):

if (r1 == 0 and r2 == 0) 
++reorders; 

Avec la CPU_FENCE personnes handicapées, ce que je vois:

[ 0][myles][~/projects/...](master) sudo ./build/ooo 
after 100000 attempts, 754 reorders observed 

Avec le CPU_FENCE permis (qui utilise l'instruction "heavyweight" mfence) Je vois:

[ 0][myles][~/projects/...](master) sudo ./build/ooo 
after 100000 attempts, 0 reorders observed 

J'espère que cela clarifie les choses pour vous!

+0

Merci beaucoup pour votre explication soigneuse et utile et surtout pour le code! Je le lance sur mon ordinateur et ça marche !!! J'apprécie vraiment votre aide! – Mike

+0

Heureux que vous l'ayez trouvé utile! –

5

Vous mélangez la condition de concurrence avec un paradigme d'exécution hors de l'ordre. Malheureusement, je suis certain que vous ne pouvez pas "exposer" l'exécution hors de l'ordre car elle est explicitement conçue et implémentée de manière à vous protéger (le programme en cours et ses données) de ses effets.

Plus précisément: l'exécution dans le désordre a lieu "à l'intérieur" d'une CPU dans son intégralité. Les résultats des instructions hors service ne sont pas directement enregistrés dans le fichier de registre, mais sont plutôt mis en file d'attente pour préserver la commande. Ainsi, même si les instructions elles-mêmes sont exécutées dans le désordre (en fonction de diverses règles qui garantissent avant tout que ces instructions puissent s'exécuter indépendamment l'une de l'autre), leurs résultats sont toujours réorganisés correctement. observateur.

Qu'est-ce que votre programme fait est: il essaie (très crûment) pour simuler une condition de course où vous espérez voir l'attribution des f à faire en même temps devant xet vous espérez avoir un contexte commutateur se produire exactement à ce moment même et vous supposez que le nouveau thread sera planifié sur le même noyau de CPU que l'autre. Cependant, comme je l'ai expliqué ci-dessus - même si vous avez la chance d'atteindre toutes les conditions listées (programmer un second thread juste après l'affectation f mais avant l'affectation et avoir le nouveau thread programmé sur le même noyau CPU) - ce qui en soi est un événement de probabilité extrêmement faible - même alors tout ce que vous exposer réellement est une condition de course potentielle, mais pas une exécution hors de l'ordre.

Désolé de vous décevoir mais votre programme ne vous aidera pas à observer les effets d'exécution hors service. Du moins pas avec une probabilité assez élevée pour être pratique.

Vous pouvez lire un peu plus sur out-of-order exécution ici: http://courses.cs.washington.edu/courses/csep548/06au/lectures/introOOO.pdf

MISE À JOUR Après avoir donné une certaine pensée, je pense que vous pouvez aller pour modifier les instructions d'une mouche dans l'espoir d'exposer l'exécution dans le désordre. Mais même alors, j'ai peur que cette approche échoue car la nouvelle instruction "mise à jour" ne sera pas correctement reflétée dans le pipeline du processeur. Ce que je veux dire, c'est que le CPU aura probablement déjà récupéré et analysé l'instruction que vous êtes sur le point de modifier, ce qui sera exécuté ne correspondra plus au contenu du mot mémoire (même celui du cache L1 de la CPU). Mais cette technique, à supposer qu'elle puisse vous aider, nécessite une programmation avancée directement dans Assembly et nécessitera que votre code s'exécute au plus haut niveau de privilège (anneau 0).Je recommande une extrême prudence avec l'écriture de code auto-modifiable, car il a un grand potentiel pour les effets secondaires.

+0

@Yephlck, Merci beaucoup pour votre explication détaillée et la correction! J'ai compris ce que vous venez de dire: le processeur va faire l'erreur dans son pipeline, mais quand il expose le résultat à l'extérieur, il est toujours réorganisé pour être dans la séquence d'origine. Sur la base de cette vérité, je pense que mon programme "ne présentera" jamais ", au lieu d'une très petite probabilité, l'effet hors-jeu, car f est toujours assigné après que x est assigné dans handler2(). Ai-je raison? – Mike

+1

Vous avez raison – YePhIcK