Modèle informatique parallèle

1. Introduction. Corutinisme compétitif


Les articles précédents sur le thème de la programmation automatique n'étaient que des «fleurs». La "baie" de la programmation automatique, c'est-à-dire pour ce que vous devez faire, est un modèle de calcul parallèle basé sur le modèle de machine d'état. Alors, allons-y ...

Le standard C ++ incluait le support tant attendu du multithreading [1]. Mais nous ne l'admirerons ni ne critiquerons ce fait, car le travail avec les threads est alourdi par tant de conditions, de mises en garde et de fonctionnalités que sans exemples réels qui révèlent des problèmes de multithreading, une discussion sur la programmation multithread sera non seulement hâtive, mais aussi assez biaisée. Par conséquent, ci-après, pas principalement sur les flux, mais sur les automates, en tenant compte, bien sûr, des premiers.

Le langage C ++ est loin d'être le premier, complété par des constructions de parallélisme. Dans les années 60 du siècle dernier, N. Wirth a proposé une extension parallèle du langage ALGOL [2]. Cependant, les 60 prochaines années n'ont pas clarifié ce qui devrait être considéré comme un algorithme parallèle et quel devrait être le modèle du calcul parallèle. Apparemment, une telle extension tardive du langage C ++ est également liée à cela.

Les constructions de longue date du langage ALGOL et leurs analogues plus modernes du langage C ++ ne sont que des méthodes de parallélisation structurelle qui n'introduisent pas de modèle algorithmique parallèle. Pour justifier cela, on peut dire que les tentatives faites au cours du temps passé pour créer un tel modèle formel de calculs ont échoué. Il suffit de dire que les mêmes filets de Petri ne justifiaient pas les grands espoirs placés sur eux.

En conséquence, la «spirale» du développement du parallélisme semble être revenue à sa source, n'ayant subi qu'un «développement terminologique». Les anciennes coroutines triviales sont devenues soudainement des «coroutines» avancées (papier calque de la coroutine anglaise), et la confusion avec les concepts de parallèle et de simultanéité dans le segment anglophone de la programmation parallèle conduit parfois à des choses paradoxales. Par exemple, la première édition du livre [1] diffère de la deuxième édition en remplaçant les termes «parallèle» par «concurrentiel» et «multithread» par «parallèle». Donc, comprenez cela dans la situation «qui est qui».

2. Modèle de calcul d'automate parallèle


Personne ne contestera probablement que la prochaine étape qualitative dans le développement de la programmation est liée à la transition vers un modèle informatique parallèle. Mais si cela se produira à la suite du développement évolutif du modèle informatique existant ou s'il s'agira d'un modèle fondamentalement différent, la question est toujours en discussion. Et si les théoriciens se disputent toujours, alors la partie pratiquement motivée des programmeurs utilise déjà des méthodes structurelles pour paralléliser les programmes.

La séparation des tâches et l'augmentation de la productivité sont considérées comme les seules raisons d'utiliser la concurrence. Au moins, pour eux ou pour leurs combinaisons, ils réduisent ou essaient de réduire tous les autres [1]. Mais il y a une raison qui est rarement évoquée, mais pour laquelle il vaut généralement la peine de s'engager dans une programmation parallèle. En effet, la vitesse peut être augmentée par des méthodes purement matérielles, et la séparation des tâches avec le parallélisme est liée de la même manière que le travail quotidien des employés de banque avec une liste de leurs fonctions officielles. Et seuls les algorithmes parallèles sont une stratégie qui nous permet de vaincre la complexité des tâches et d'augmenter la fiabilité des programmes. Et tout cela est contraire à l'opinion dominante concernant la programmation multi-thread, qui transforme tout programme parallèle en un produit logiciel complexe et peu fiable.

Un système parallèle, composé de nombreux composants, objets, agents, etc. fonctionnant en parallèle et interagissant activement, met en œuvre un algorithme qui est déterminé de nombreuses manières non pas par les algorithmes des composants individuels (bien qu'ils le soient, bien sûr), mais par le nombre de composants, le nombre et genre de connexions entre eux. Pour contrôler cette complexité et comprendre l'algorithme de fonctionnement du système parallèle, vous avez besoin non seulement d'un modèle de calcul parallèle, mais d'un modèle qui a, entre autres, et même surtout, une théorie correspondante.

La thèse selon laquelle «souvent un programme parallèle est plus difficile à comprendre ... et, par conséquent, le nombre d'erreurs augmente», pour le moins, est discutable. Oui, l'algorithme de programme parallèle peut être assez difficile à comprendre, mais s'il existe une théorie, il peut être "calculé" formellement en utilisant des algorithmes de composants. Et du point de vue de la conception, de la mise en œuvre et de la maintenance, les algorithmes des composants sont beaucoup plus simples que l'algorithme du système dans son ensemble. Lors de la conception de composants plus simples, nous ferons évidemment moins d'erreurs que la conception d'un système en une seule pièce. De plus, les composants débogués peuvent faire partie d'autres systèmes, réduisant la complexité, augmentant la fiabilité et minimisant leurs coûts de conception.

3. Accès simultané en série


L'article [3] décrit le parallélisme d'un modèle distinct d'une machine à états finis. Ses canaux au niveau de l'exécution des transitions spécifient l'exécution parallèle des fonctions / méthodes qui lui sont associées - prédicats et actions. Dans le même temps, il n'y a aucune restriction sur le parallélisme des prédicats. Quand ils travaillent, ils n'entrent pas en conflit les uns avec les autres, car n'affectent pas le contenu de la mémoire. Les actions, fonctionnant en parallèle, peuvent avoir des données d'entrée et de sortie communes, ainsi que les modifier indépendamment les unes des autres. Et tout cela peut être une source d'incertitude dans la valeur des données de sortie.

Le bon fonctionnement des actions dans les situations décrites ci-dessus fournit une mémoire fantôme. En y stockant de nouvelles valeurs, on peut utiliser les mêmes données dans une même action, à la fois en entrée et en sortie. Un exemple est le modèle d'un générateur d'impulsions rectangulaire, décrit comme y =! Y, où y est la sortie du générateur. Son code C ++ dans l'environnement VKPa est montré dans le Listing 1, et les résultats du programme sont montrés dans la Fig. 1.

Listing 1. Générateur d'impulsions rectangulaire
#include "lfsaappl.h"

class FSWGenerator :
    public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FSWGenerator(pTAppCore, nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();
    FSWGenerator(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL);
    virtual ~FSWGenerator(void) {};
    CVar *pVarY;				//  
protected:
    void y1();
};

#include "stdafx.h"
#include "FSWGenerator.h"
// state machine transition table
static LArc TBL_SWGenerator[] = {
    LArc("s1",		"s1","--",	"y1"),			//
    LArc()
};

FSWGenerator::FSWGenerator(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL):
    LFsaAppl(TBL_SWGenerator, strNam, nullptr, pCVFL)
{
    pTAppCore = pInfo;
}
// creating local variables and initialization of pointers
bool FSWGenerator::FCreationOfLinksForVariables() {
// creating local variables
    pVarY = CreateLocVar("y", CLocVar::vtBool, " ");
    return true;
}
// setting output signals
void FSWGenerator::y1() {
    pVarY->SetDataSrc(nullptr, !bool(pVarY->GetDataSrc()));
}


image
Figure. 1. Simulation du fonctionnement du générateur d'impulsions rectangulaire dans le VKPA La

machine automatique a dans ce cas une condition avec une transition inconditionnelle (une transition avec un tiret à la place de la condition d'entrée) sous la forme d'une boucle marquée par l'action y1, qui met en œuvre l'inversion de la variable de sortie, qui forme une impulsion rectangulaire en dynamique. Dans le cadre du modèle d'automate, la fréquence du signal impulsionnel peut être contrôlée en réglant la valeur de tact du temps discret de l'espace d'automate dans lequel l'automate est chargé.

1. , . . . .


La capacité de contrôler le temps discret de l'automate et la présence de nombreux espaces d'automate ne sont pas les seules, mais importantes, propriétés distinctives de l'environnement VKPa. En les utilisant, vous pouvez optimiser les performances d'un programme parallèle. Par exemple, les machines qui implémentent la visualisation des données et les dialogues utilisateur doivent être placées dans des espaces d'automate lents, et les processus d'application doivent être répartis entre les espaces d'automate en fonction des priorités et de la vitesse souhaitée, etc. etc.

Dans le cadre d'un modèle d'automate, la valeur de la sortie du générateur est facilement liée à l'état actuel du modèle. Le code du modèle de générateur, qui a déjà deux états, chacun reflétant l'état de la sortie du générateur, est indiqué dans le listing 2.

Listing 2. Générateur d'ondes carrées sur les états
#include "lfsaappl.h"

extern LArc TBL_SWGenState[];
class FSWGenState :
    public LFsaAppl
{
public:
    FSWGenState(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL):
    LFsaAppl(TBL_SWGenState, strNam, nullptr, pCVFL) {};
};

#include "stdafx.h"
#include "FSWGenState.h"
// state machine transition table
LArc TBL_SWGenState[] = {
    LArc("s0",		"s1","--",	"--"),			//
    LArc("s1",		"s0","--",	"--"),			//
    LArc()
};


Dans le nouveau modèle, les états remplacent la variable de sortie, ce qui, comme on peut le voir, simplifie considérablement le modèle du générateur. En conséquence, nous avons obtenu une machine «nue», représentée uniquement par une table de conversion. Pour surveiller son état actuel «s1» dans VKPa, une variable de type fsa (état) avec le nom SWGenState. (S1) a été créée pour la machine avec le nom SWGenState. Il prend une valeur vraie dans l'état s1 et fausse lorsque la machine est dans un état différent. En outre, cette variable est déjà utilisée au moyen de l'affichage des données de l'environnement VKPA (voir la tendance du signal sur la figure 2).

image
Figure. 2. Modélisation du générateur d'état

4. Modèle de contrôle informatique parallèle


De plus, en allant vers la création d'un modèle de processus parallèles, il est logique d'utiliser de nombreuses machines à états finis fonctionnant et interagissant simultanément, c'est-à-dire réseau d'automates. Dans ce cas, le problème du choix d'un modèle de temps réseau apparaît, qui peut être le même pour toutes les machines ou, à la limite, individuel pour chacune. En VKPa, le choix a été fait en faveur d'un temps unique (pour plus de détails sur les réseaux synchrones d'automates, voir [5]).

Le choix d'un temps unique vous permet de créer une algèbre d'automates ayant des opérations de composition et de décomposition d'automates. En utilisant le premier, vous pouvez trouver l'automate résultant, qui donne une idée précise du fonctionnement d'un système parallèle. Et ici, il convient de rappeler la thèse ci-dessus sur la «complexité de la compréhension» des programmes parallèles. La présence de l'opération de composition nous permet de résoudre le "problème de compréhension" du programme parallèle.

Bien entendu, l'automate résultant pour un réseau d'un grand nombre de composants peut être très volumineux. Mais, heureusement, une compréhension du fonctionnement des sous-systèmes ou réseaux d'un petit nombre de composants est plus souvent requise, pour laquelle la recherche de l'automate résultant ne pose pas de gros problèmes. L'exemple de modèle RS-flip-flop suivant le démontre.

Le modèle de déclenchement RS est un exemple d'un système parallèle simple. C'est particulièrement intéressant en présence de rétroactions croisées. Feedbacks, ou, d'une autre manière, chaînes cycliques, boucles, boucles algébriques, etc. sont actuellement un problème sérieux pour les modèles structurels de systèmes parallèles. Dans le cas général, elle est permise en introduisant dans les boucles d'espace des éléments de mémoire. C'est la solution standard proposée par la théorie des automates [4]. La même sortie est recommandée en la personne de MATLAB. L'environnement VKPa est différent en ce qu'il ne nécessite pas l'introduction de tels éléments supplémentaires pour la mise en œuvre des boucles. Notez, et c'est très important, que les circuits réels n'en ont pas besoin non plus (voir le circuit RS-flip-flop).

En figue. La figure 3 présente le modèle le plus simple de l'élément AND-NOT, qui consiste en le circuit de déclenchement RS. Il ne prend pas en compte les retards des éléments, ainsi que leur type (transport ou retards inertiels). Cependant, il contient toujours au moins un temps de retard. C'est le moment de la transition d'un état à un autre. Le listing 3 montre le code du modèle

image
. 3. Le modèle de l'élément ET NON

Listing 3. Modèle d'élément ET NON
#include "lfsaappl.h"

class FIne :
    public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FIne(pTAppCore, nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();
    FIne(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL);
    virtual ~FIne(void) {};
    CVar *pVarX1;				//  
    CVar *pVarX2;				//  
    CVar *pVarY;				//  
    CVar *pVarStrNameX1;		//    
    CVar *pVarStrNameX2;		//    
    CVar *pVarStrNameY;         //   
protected:
    int x1(); int x2(); int x12();
    void y1(); void y2(); void y12();
    bool bX1, bX2, bY;
};


#include "stdafx.h"
#include "FIne.h"
// state machine transition table
static LArc TBL_Ine[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"s1","x12^x1",	"y1"),		//
    LArc("st",		"s1","x12^x2",	"y1"),		//
    LArc("st",		"s0","x12x1x2",	"y2"),		//
    LArc("s1",		"s0","x1x2",   "y2"),		//
    LArc("s0",		"s1","^x1",    "y1"),		//
    LArc("s0",		"s1","^x2",    "y1"),		//
    LArc()
};

FIne::FIne(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL):
    LFsaAppl(TBL_Ine, strNam, nullptr, pCVFL)
{ }

// creating local variables and initialization of pointers
bool FIne::FCreationOfLinksForVariables() {
// creating local variables
    pVarX1 = CreateLocVar("x1", CLocVar::vtBool, " 1- ");
    pVarX2 = CreateLocVar("x2", CLocVar::vtBool, " 2- ");
    pVarY = CreateLocVar("y", CLocVar::vtBool, " ");
    pVarStrNameX1 = CreateLocVar("strNameX1", CLocVar::vtString, "name of external input variable(x1)");			//   
    pVarStrNameX2 = CreateLocVar("strNameX2", CLocVar::vtString, "name of external input variable(x2)");			//   
    pVarStrNameY = CreateLocVar("strNameY", CLocVar::vtString, "name of external output variable(y)");		//   
// initialization of pointers
    string str;
    if (pVarStrNameX1) {
        str = pVarStrNameX1->strGetDataSrc();
        if (str != "") { pVarX1 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    }
    if (pVarStrNameX2) {
        str = pVarStrNameX2->strGetDataSrc();
        if (str != "") { pVarX2 = pTAppCore->GetAddressVar(str.c_str(), this); }
    }
    if (pVarStrNameY) {
        str = pVarStrNameY->strGetDataSrc();
        if (str != ""){pVarY = pTAppCore->GetAddressVar(str.c_str(), this);}
    }
    return true;
}

int FIne::x1() { return bool(pVarX1->GetDataSrc()); }
int FIne::x2() { return bool(pVarX2->GetDataSrc()); }
int FIne::x12() { return pVarX1 != nullptr && pVarX2 && pVarY; }

void FIne::y1() { pVarY->SetDataSrc(nullptr, 1); }
void FIne::y2() { pVarY->SetDataSrc(nullptr, 0.0); }
void FIne::y12() { FInit(); }


En figue. La figure 4 montre un schéma d'une bascule RS et son modèle sous la forme d'une machine à états finis. Les flèches sur le modèle indiquent les connexions entre les machines du réseau. Ici, d'une part, les états des modèles reflètent les états des sorties de l'élément et, d'autre part, ils sont également utilisés comme signaux pour organiser des liens d'information entre des processus parallèles. C'est cette forme du modèle (avec synchronisation par états) qui permet de trouver assez facilement l'automate résultant du réseau. Il est illustré sur la fig. 5 (pour la procédure de recherche de l'automate résultant, voir [6] pour plus de détails).

Comparez l'algorithme [résultant] du programme parallèle de bascule RS et l'algorithme de fonctionnement d'un élément AND-NOT séparé. La différence est frappante. Dans ce cas, les algorithmes des composants sont créés par des «poignées» et l'algorithme du système parallèle est créé implicitement - par «l'intelligence artificielle» du réseau. C'est la différence qualitative entre les programmes parallèles et séquentiels: en ne changeant que les communications (au moins une), nous obtiendrons un algorithme de travail complètement différent. Et ce ne sera certainement plus un déclencheur RS. Et, soit dit en passant, un autre automate résultant.

image
Figure. 4. Schéma du modèle RS-FF et du réseau

image
Fig. 5. Le modèle de réseau de machines résultant RS-trigger

L'analyse de l'automate résultant sur la Fig. 5 donne la "compréhension" suivante du programme parallèle (et du déclencheur réel, bien sûr). Premièrement, lors du passage d'un état à un autre, le déclencheur passera nécessairement par l'état «interdit» des sorties (et qu'en disent les manuels?). Deuxièmement, si le déclencheur est entraîné dans un seul état de sorties (dans l'état "s1w1"), puis que deux unités sont alimentées aux entrées, il passera en mode de génération, c'est-à-dire commutation cyclique entre les états «s1w1» et «s0w0» et (et avez-vous entendu parler de la génération de déclencheurs?).

Une transition à travers un état interdit se produit également dans un déclencheur réel, mais le mode de génération est impossible en raison de la différence de retards d'éléments réels. Figure. La figure 6 montre le mode de génération du modèle de déclenchement déclencheur, qui existe tant que les unités aux entrées sont stockées.

Remarque 2. Une description typique du fonctionnement du déclencheur RS est donnée dans la très grande majorité des cas sous la forme d'une table de vérité. Mais pour ce faire, comprendre qu'un déclencheur est un schéma séquentiel, c'est en fait délibérément induire en erreur ceux qui étudient ce sujet. Eh bien, aucun déclencheur ne peut avoir des «états interdits»! Mais pour une raison quelconque, seuls quelques-uns décident de découvrir cette vérité et, en particulier, discutent du problème de sa génération (voir, par exemple, [7]).


Figure. 7 montre la commutation d'un modèle de déclenchement entre ses états stables. Ici, un seul état des entrées de déclenchement conserve l'état actuel des sorties de déclenchement et lorsque telle ou telle entrée est mise à zéro, elle passe à l'état opposé. En même temps, lorsque le déclencheur est commuté, ses sorties momentanément égales à une mesure discrète prennent en même temps un seul état (interdit par qui?).

image
Figure. 6. Mode de génération RS-trigger

image
Fig. 7. Commutation RS-trigger entre les états

Considérons un autre modèle RS-trigger, composé d'un état et d'une action, c'est-à-dire similaire au modèle du Listing 1. Son code est montré dans le Listing 4. Ce modèle, comme le modèle générateur, n'a pas de prédicats et les valeurs du signal sans aucune transformation intermédiaire sont entrées dans l'action y1. Est-ce bon ou mauvais? D'une part, il semble que ce soit bon, car le code est devenu plus simple, mais d'un autre côté ... pas vraiment. Et nous en comprendrons maintenant les raisons.

Listing 4. Modèle d'élément NAND à partir d'une seule action
#include "lfsaappl.h"

class FTwoOperators :
    public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FTwoOperators(pTAppCore, nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();
    FTwoOperators(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL);
    virtual ~FTwoOperators(void) {};
    CVar *pVarX1;				//  
    CVar *pVarX2;				//  
    CVar *pVarY;				//  
    CVar *pVarStrNameX1;		//    
    CVar *pVarStrNameX2;		//    
    CVar *pVarStrNameY;         //   
protected:
    int x12();
    void y1(); void y12();
    bool bX1, bX2, bY;
};

#include "stdafx.h"
#include "FTwoOperators.h"
// state machine transition table
static LArc TBL_TwoOperators[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"s1","x12", "--"),		//
    LArc("s1",		"s1","--",  "y1"),		//
    LArc()
};

FTwoOperators::FTwoOperators(TAppCore *pInfo, string strNam, CVarFsaLibrary *pCVFL):
    LFsaAppl(TBL_TwoOperators, strNam, nullptr, pCVFL)
{ }
// creating local variables and initialization of pointers
bool FTwoOperators::FCreationOfLinksForVariables() {
// creating local variables
    pVarX1 = CreateLocVar("x1", CLocVar::vtBool, " 1- ");
    pVarX2 = CreateLocVar("x2", CLocVar::vtBool, " 2- ");
    pVarY = CreateLocVar("y", CLocVar::vtBool, " ");
    pVarStrNameX1 = CreateLocVar("strNameX1", CLocVar::vtString, "name of external input variable(x1)");			//   
    pVarStrNameX2 = CreateLocVar("strNameX2", CLocVar::vtString, "name of external input variable(x2)");			//   
    pVarStrNameY = CreateLocVar("strNameY", CLocVar::vtString, "name of external output variable(y)");		//   
// initialization of pointers
    string str;
    if (pVarStrNameX1) {
        str = pVarStrNameX1->strGetDataSrc();
        if (str != "") { pVarX1 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    }
    if (pVarStrNameX2) {
        str = pVarStrNameX2->strGetDataSrc();
        if (str != "") { pVarX2 = pTAppCore->GetAddressVar(str.c_str(), this); }
    }
    if (pVarStrNameY) {
        str = pVarStrNameY->strGetDataSrc();
        if (str != "") { pVarY = pTAppCore->GetAddressVar(str.c_str(), this);	}
    }
    return true;
}

int FTwoOperators::x12() { return pVarX1 != nullptr && pVarX2 && pVarY; }

void FTwoOperators::y1() {
// reading input signals
    bX1 = bool(pVarX1->GetDataSrc());
    bX2 = bool(pVarX2->GetDataSrc());
// setting output signals
    bY = !(bX1&&bX2);
    pVarY->SetDataSrc(nullptr, bY);
}
// initialization of pointers
void FTwoOperators::y12() { FInit(); }


Si nous testons le nouveau modèle en mode «mémoire fantôme», nous ne verrons aucune différence dans son fonctionnement par rapport au précédent, c'est-à-dire, et, en passant, il passera par les états interdits et passera régulièrement en mode génération. Si nous configurons le travail avec des données dans le mode habituel, nous obtiendrons les résultats indiqués sur la Fig. 8 et fig. 9.

image
Fig. 8. Échec du mode de génération du modèle RS-trigger

image
. 9. Ignorer les états interdits par le

modèle RS-trigger Pourquoi le premier modèle, quel que soit le mode de travail avec la mémoire, affiche-t-il des résultats stables et le second - change-t-il de comportement? La raison en est les prédicats. Le deuxième modèle n'a pas de prédicats, ce qui est essentiel pour son comportement. Mais comment et pourquoi la présence / l'absence de prédicats affecte-t-elle l'algorithme de fonctionnement du programme parallèle?

Le modèle de programme d'un élément AND-NOT, comme un programme automate, a deux canaux d'entrée et un canal de sortie. Ils doivent correspondre à deux prédicats et une action. Le premier programme est parfaitement conforme à cela. Le noyau VKPa, qui interprète la description de l'automate, exécute d'abord tous les prédicats non seulement d'un automate particulier, mais également de tout l'espace de l'automate, puis démarre toutes les actions. Dans ce cas, quelle que soit la séquence dans laquelle les actions ont été exécutées, simulant le parallélisme, et quel que soit le mode de fonctionnement avec la mémoire, les résultats des prédicats sur le cycle d'horloge actuel de l'automate ne dépendent pas d'eux (actions). Par conséquent, le premier programme produit le même résultat.

Le deuxième programme, bien qu'il fonctionne directement avec les canaux d'entrée de la machine, lit les signaux d'entrée dans le cadre de l'action. Les actions, en travaillant avec des données d'entrée en mode de mémoire fantôme, écrivent de nouvelles valeurs dans la mémoire fantôme et travaillent ainsi avec des données valides au début d'un cycle d'horloge discret. Dans le mode habituel, ils «saisissent» les valeurs instantanées établies au moment de leur changement, et donc l'algorithme devient dépendant des moments de changement de mémoire. Une dépendance similaire est démontrée par le deuxième programme. Et même si des méthodes de prédicat étaient introduites dans le deuxième modèle, cela n'aurait aucun effet sur les résultats de ses travaux. Ce qui importe ici n'est pas le fait de l'existence de méthodes de prédicat, mais les particularités de leur travail dans le cadre du modèle de programmation automate.

5. Conclusions


En utilisant le programme parallèle RS-trigger comme exemple, nous avons examiné certaines des propriétés inhérentes à tout programme parallèle. Nous continuerons à considérer certains aspects généraux du fonctionnement des programmes parallèles comme exemple de circuits logiques (numériques). Le choix du thème de la modélisation des circuits numériques n'est pas accidentel. En fait, sous «forme raffinée», ils représentent le travail de processus parallèles. Cela rend l'analyse des nuances de la concurrence, de la race, de la synchronisation, des impasses, etc. etc. transparent, clair et simple.

En même temps, peu importe comment vous appelez la programmation - «compétitive» ou parallèle, que vous utilisiez des «coroutines», des coroutines, des threads ou des machines pour la programmation, le résultat du programme [parallèle] doit être le même dans toutes les implémentations. Le modèle automatique des programmes parallèles dans le cadre du PCUS poursuit cet objectif et uniquement cet objectif.
Quelles que soient les hypothèses qui seraient faites concernant la mise en œuvre du cœur de l’interprétation des automates de l’environnement du VKPa, toutes ces hypothèses seraient des «spéculations», car le résultat du travail des programmes automatiques ne doit pas être associé à la mise en œuvre d'un modèle de calcul. Il peut s'agir de logiciels (comme c'est le cas actuellement) ou de matériel (comme je l'espère dans le futur), implémentés sur un cœur ou sur leur ensemble, en version mono-thread ou multi-thread, etc. etc. tout cela ne devrait en aucun cas affecter les résultats des programmes automatiques parallèles.

Et, semble-t-il, l'objectif a été atteint. Le modèle RS-trigger, comme l'un des tests possibles des systèmes de parallélisme [8], nous en convainc ... Comme la vie l'a montré, tous les autres programmes parallèles, à condition que l'environnement ait réussi la mise en œuvre du parallélisme du test RS-trigger, fonctionnent tout aussi correctement, de manière fiable et stable. . Soit dit en passant, le même «test de déclenchement RS» MATLAB n'a pas réussi, et cela en dit déjà beaucoup ...

Littérature
1. . ++ . . . . .. – .: , 2012. – 672 .
2. . : . . – .: , 1981. – 360 .
3. . [ ], : habr.com/ru/post/484588 . . . ( 07.01.2020).
4. .. . .: , 1962.
5. .., - .. . – 2- ., . . – .: , 1988. – 480 .
6. .. . [ ], : cloud.mail.ru/public/HwsK/T95PMM8Ed . . . ( 01.02.2020).
7. . . 2- . – .: , 2004. – 432.
8. .. ? “ ”, №10/97, .116-119. [ ], : www.osp.ru/pcworld/1997/10/158015 . . . ( 01.02.2020).

Source: https://habr.com/ru/post/undefined/


All Articles