Paralleles Rechenmodell

1. Einleitung. Wettbewerbskorutinismus


Frühere Artikel zum Thema automatische Programmierung waren nur „Blumen“. Die "Beere" der automatischen Programmierung, d.h. Für das, was Sie tun müssen, ist ein Modell des parallelen Rechnens basierend auf dem Zustandsmaschinenmodell. Also, lass uns gehen ...

Der C ++ - Standard enthielt die lang erwartete Unterstützung für Multithreading [1]. Aber wir werden es weder bewundern noch kritisieren, weil Die Arbeit mit Threads wird durch so viele Bedingungen, Vorbehalte und Funktionen belastet, dass eine Diskussion über Multithread-Programmierung ohne Beispiele aus der Praxis, die Multithreading-Probleme aufzeigen, nicht nur voreilig, sondern auch ziemlich voreingenommen sein wird. Daher im Folgenden hauptsächlich nicht über Flüsse, sondern über Automaten, wobei natürlich die ersten berücksichtigt werden.

Die C ++ - Sprache ist weit von der ersten entfernt und wird durch Parallelitätskonstrukte ergänzt. In den 60er Jahren des letzten Jahrhunderts schlug N. Wirth eine parallele Erweiterung der ALGOL-Sprache vor [2]. Die nächsten 60 Jahre haben jedoch nicht geklärt, was als paralleler Algorithmus und was das Modell des parallelen Rechnens sein sollte. Anscheinend ist damit auch eine solche verspätete Erweiterung der C ++ - Sprache verbunden.

Sowohl die langjährigen Konstrukte der ALGOL-Sprache als auch ihre moderneren Analoga in der C ++ - Sprache sind nur strukturelle Parallelisierungsmethoden, die kein paralleles algorithmisches Modell einführen. Um dies zu rechtfertigen, kann gesagt werden, dass die in der vergangenen Zeit unternommenen Versuche, ein solches formales Berechnungsmodell zu erstellen, gescheitert sind. Es genügt zu sagen, dass dieselben Petri-Netze die großen Hoffnungen, die auf sie gesetzt wurden, nicht rechtfertigten.

Infolgedessen scheint die „Spirale“ der Parallelitätsentwicklung zu ihrer Quelle zurückgekehrt zu sein, da sie nur eine „terminologische Entwicklung“ durchlaufen hat. Ehemalige triviale Coroutinen wurden plötzlich zu fortgeschrittenen „Coroutinen“ (Transparentpapier der englischen Coroutine), und die Verwechslung mit den Konzepten der Parallelität und Gleichzeitigkeit im englischsprachigen Segment der parallelen Programmierung führt manchmal zu paradoxen Dingen. Beispielsweise unterscheidet sich die erste Ausgabe des Buches [1] von der zweiten Ausgabe, indem die Begriffe "parallel" durch "wettbewerbsfähig" und "multithreaded" durch "parallel" ersetzt werden. Finden Sie dies in der Situation „Wer ist wer?“ Heraus.

2. Paralleles Automatenmodell von Berechnungen


Wahrscheinlich wird niemand bestreiten, dass der nächste qualitative Schritt in der Entwicklung der Programmierung mit dem Übergang zu einem parallelen Rechenmodell verbunden ist. Ob dies jedoch aufgrund der evolutionären Entwicklung des bestehenden Rechenmodells geschehen wird oder ob es sich um ein grundlegend anderes Modell handelt, wird derzeit noch diskutiert. Und wenn Theoretiker immer noch streiten, dann verwendet der praktisch motivierte Teil der Programmierer bereits strukturelle Methoden zur Parallelisierung von Programmen.

Aufgabentrennung und Produktivitätssteigerung werden als einzige Gründe für die Verwendung von Parallelität angesehen. Zumindest für sie oder ihre Kombinationen reduzieren oder versuchen letztendlich alle anderen zu reduzieren [1]. Es gibt jedoch einen Grund, über den selten gesprochen wird, weshalb es sich im Allgemeinen lohnt, parallel zu programmieren. In der Tat kann die Geschwindigkeit durch reine Hardwaremethoden erhöht werden, und die Aufgabentrennung mit Parallelität ist auf die gleiche Weise verbunden wie die tägliche Arbeit der Bankangestellten mit einer Liste ihrer offiziellen Aufgaben. Und nur parallele Algorithmen sind eine Strategie, mit der wir die Komplexität von Aufgaben überwinden und die Zuverlässigkeit von Programmen erhöhen können. Und all dies widerspricht der vorherrschenden Meinung bezüglich der Multithread-Programmierung, die jedes parallele Programm in ein komplexes und unzuverlässiges Softwareprodukt verwandelt.

Ein paralleles System, das aus vielen parallel funktionierenden und aktiv interagierenden Komponenten, Objekten, Agenten usw. besteht, implementiert einen Algorithmus, der in vielerlei Hinsicht nicht durch die Algorithmen der einzelnen Komponenten (obwohl natürlich auch), sondern durch die Anzahl der Komponenten, die Anzahl und bestimmt wird Art von Verbindungen zwischen ihnen. Um diese Komplexität zu kontrollieren und den Algorithmus des parallelen Systems zu verstehen, benötigen Sie nicht nur ein paralleles Rechenmodell, sondern ein Modell, das unter anderem und vor allem eine geeignete Theorie aufweist.

Die These, dass „ein paralleles Programm oft schwieriger zu verstehen ist ... und daher die Anzahl der Fehler zunimmt“, um es milde auszudrücken, ist umstritten. Ja, der Parallelprogrammalgorithmus kann ziemlich schwer zu verstehen sein, aber wenn es eine Theorie gibt, kann er unter Verwendung von Komponentenalgorithmen formal "berechnet" werden. Unter dem Gesichtspunkt des Entwurfs, der Implementierung und der Wartung der Komponentenalgorithmen ist dies viel einfacher als der Algorithmus des gesamten Systems. Beim Entwerfen einfacherer Komponenten machen wir offensichtlich weniger Fehler als beim Entwerfen eines Systems in einem Stück. Darüber hinaus können debuggte Komponenten Teil anderer Systeme sein, wodurch die Komplexität verringert, die Zuverlässigkeit erhöht und die Konstruktionskosten minimiert werden.

3. Serielle Parallelität


Der Artikel [3] beschrieb die Parallelität eines separaten Modells einer endlichen Zustandsmaschine. Seine Kanäle auf der Ebene der Ausführung von Übergängen spezifizieren die parallele Ausführung der damit verbundenen Funktionen / Methoden - Prädikate und Aktionen. Gleichzeitig gibt es keine Einschränkungen für die Prädikatparallelität. Bei der Arbeit stehen sie nicht in Konflikt miteinander, weil Beeinflussen Sie nicht den Inhalt des Speichers. Parallel arbeitende Aktionen können gemeinsame Eingabe- und Ausgabedaten haben und diese unabhängig voneinander ändern. All dies kann zu Unsicherheiten im Wert der Ausgabedaten führen.

Die korrekte Ausführung von Aktionen in den oben beschriebenen Situationen bietet Schattenspeicher. Durch Speichern neuer Werte können dieselben Daten innerhalb einer einzigen Aktion sowohl als Eingabe als auch als Ausgabe verwendet werden. Ein Beispiel ist das Modell eines Rechteckimpulsgenerators, beschrieben als y =! Y, wobei y der Ausgang des Generators ist. Der C ++ - Code in der VKPa-Umgebung ist in Listing 1 dargestellt, und die Ergebnisse des Programms sind in Abb. 1 dargestellt. 1.

Listing 1. Rechteckiger Impulsgeber
#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()));
}


Bild
Feige. 1. Simulation des Betriebs des Rechteckimpulsgenerators im VKPA

In diesem Fall hat die Maschine einen Zustand mit einem bedingungslosen Ăśbergang (ein Ăśbergang mit einem Strich anstelle der Eingangsbedingung) in Form einer Schleife, die durch die Aktion y1 gekennzeichnet ist und die Inversion der Ausgangsvariablen implementiert, die in der Dynamik einen Rechteckimpuls bildet. Im Rahmen des Automatenmodells kann die Frequenz des Impulssignals gesteuert werden, indem der Taktwert der diskreten Zeit des Automatenraums eingestellt wird, in den der Automat geladen wird.

1. , . . . .


Die Fähigkeit, die diskrete Zeit des Automaten zu steuern, und das Vorhandensein vieler Automatenräume sind nicht die einzigen, aber wichtigen charakteristischen Eigenschaften der VKPa-Umgebung. Mit ihnen können Sie die Leistung eines Parallelprogramms optimieren. Beispielsweise sollten Maschinen, die Datenvisualisierung und Benutzerdialoge implementieren, in langsamen Automatenräumen platziert werden, und Anwendungsprozesse sollten entsprechend den Prioritäten und ihrer gewünschten Geschwindigkeit usw. auf Automatenräume verteilt werden. usw.

Im Rahmen eines Automatenmodells kann der Wert der Generatorleistung leicht mit dem aktuellen Zustand des Modells in Beziehung gesetzt werden. Der Code für das Generatormodell, das bereits zwei Zustände aufweist, von denen jeder den Zustand des Generatorausgangs widerspiegelt, ist in Listing 2 dargestellt.

Listing 2. Rechteckwellengenerator für Zustände
#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()
};


In dem neuen Modell ersetzen Zustände die Ausgangsvariable, und dies hat, wie zu sehen ist, das Generatormodell dramatisch vereinfacht. Als Ergebnis haben wir eine "nackte" Maschine erhalten, die nur durch eine Konvertierungstabelle dargestellt wird. Um den aktuellen Status „s1“ in VKPa zu überwachen, wurde für die Maschine mit dem Namen SWGenState eine Variable vom Typ fsa (Status) mit dem Namen SWGenState (S1) erstellt. Es nimmt den wahren Wert im Zustand s1 und den falschen Wert an, wenn sich die Maschine in einem anderen Zustand befindet. Ferner wird diese Variable bereits mittels der Anzeige der Daten der VKPA-Umgebung verwendet (siehe den Signaltrend in Fig. 2).

Bild
Feige. 2. Modellierung des Zustandsgenerators

4. Paralleles Rechensteuerungsmodell


Um ein Modell paralleler Prozesse zu erstellen, ist es ferner logisch, viele gleichzeitig funktionierende und interagierende Finite-State-Maschinen zu verwenden, d. H. Netzwerk von Automaten. In diesem Fall tritt das Problem der Auswahl eines Netzwerkzeitmodells auf, das fĂĽr alle Maschinen gleich oder im Einzelfall fĂĽr jede Maschine gleich sein kann. In VKPa wurde die Wahl zugunsten eines einzelnen Zeitpunkts getroffen (weitere Einzelheiten zu synchronen Netzwerken von Automaten siehe [5]).

Durch die Auswahl eines einzelnen Zeitpunkts können Sie eine Algebra von Automaten mit Operationen zum Zusammensetzen und Zerlegen von Automaten erstellen. Mit dem ersten können Sie den resultierenden Automaten finden, der eine genaue Vorstellung vom Betrieb eines parallelen Systems gibt. Und hier sei an die obige These über die "Komplexität des Verständnisses" paralleler Programme erinnert. Das Vorhandensein der Kompositionsoperation ermöglicht es uns, das "Problem des Verstehens" des Parallelprogramms zu lösen.

Natürlich kann der resultierende Automat für ein Netzwerk einer großen Anzahl von Komponenten sehr umfangreich sein. Glücklicherweise ist jedoch häufiger ein Verständnis der Funktionsweise von Subsystemen oder Netzwerken einer kleinen Anzahl von Komponenten erforderlich, für die das Auffinden des resultierenden Automaten keine großen Probleme verursacht. Das folgende Beispiel eines RS-Flip-Flop-Modells zeigt dies.

Das RS-Trigger-Modell ist ein Beispiel für ein einfaches paralleles System. Es ist besonders interessant bei Kreuzrückkopplungen. Rückkopplungen oder auf andere Weise zyklische Ketten, Schleifen, algebraische Schleifen usw. sind derzeit ein ernstes Problem für Strukturmodelle paralleler Systeme. Im allgemeinen Fall ist dies durch Einführen von Speicherelementen in die Lückenschleifen zulässig. Dies ist die von der Automatentheorie vorgeschlagene Standardlösung [4]. Die gleiche Ausgabe wird in der Person von MATLAB empfohlen. Die VKPa-Umgebung unterscheidet sich darin, dass solche zusätzlichen Elemente für die Implementierung von Schleifen nicht eingeführt werden müssen. Beachten Sie, und dies ist sehr wichtig, dass echte Schaltungen sie auch nicht benötigen (siehe die RS-Flip-Flop-Schaltung).

In Abb. Abbildung 3 zeigt das einfachste Modell des AND-NOT-Elements, aus dem die RS-Triggerschaltung besteht. Elementverzögerungen sowie deren Art (Transport- oder Trägheitsverzögerungen) werden nicht berücksichtigt. Es enthält jedoch immer noch mindestens einen Verzögerungsschlag. Dies ist die Zeit des Übergangs von einem Zustand in einen anderen. Listing 3 zeigt den Modellcode

Bild
. 3. Das Modell des Elements UND NICHT

Listing 3. Elementmodell UND NICHT
#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(); }


In Abb. Fig. 4 zeigt ein Diagramm eines RS-Flipflops und seines Modells in Form einer Finite-State-Maschine. Die Pfeile auf dem Modell geben die Verbindungen zwischen den Automaten des Netzwerks an. Hier spiegeln einerseits die Zustände der Modelle die Zustände der Ausgänge des Elements wider und andererseits werden sie auch als Signale zum Organisieren von Informationsverbindungen zwischen parallelen Prozessen verwendet. Es ist diese Form des Modells (mit Synchronisation durch Zustände), die es ziemlich einfach macht, den resultierenden Automaten des Netzwerks zu finden. Es ist in Abb. 1 dargestellt. 5 (Informationen zum Auffinden des resultierenden Automaten finden Sie in [6] für weitere Einzelheiten).

Vergleichen Sie den [resultierenden] Algorithmus des parallelen RS-Flip-Flop-Programms und den Operationsalgorithmus eines separaten AND-NOT-Elements. Der Unterschied ist auffällig. In diesem Fall werden die Komponentenalgorithmen durch "Handles" erstellt, und der parallele Systemalgorithmus wird implizit erstellt - durch die "künstliche Intelligenz" des Netzwerks. Dies ist der qualitative Unterschied zwischen parallelen und sequentiellen Programmen: Wenn nur die Kommunikation (mindestens eines) geändert wird, erhalten wir einen völlig anderen Arbeitsalgorithmus. Und es wird definitiv kein RS-Trigger mehr sein. Und übrigens ein weiterer resultierender Automat.

Bild
Feige. 4. Schema des RS-FF- und Netzwerkmodells

Bild
Abb. 5. Der resultierende Maschinennetzwerkmodell-RS-Trigger

Die Analyse des resultierenden Automaten in Abb. 5 gibt das folgende "Verständnis" des Parallelprogramms (und natürlich des eigentlichen Auslösers). Erstens durchläuft der Trigger beim Wechsel von einem Zustand in einen anderen notwendigerweise den „verbotenen“ Zustand der Ausgänge (und was sagen die Lehrbücher dazu?). Zweitens, wenn der Trigger in einen einzelnen Ausgangszustand (im Zustand "s1w1") getrieben wird und dann zwei Einheiten den Eingängen zugeführt werden, tritt er in den Erzeugungsmodus ein, d.h. zyklisches Umschalten zwischen den Zuständen „s1w1“ und „s0w0“ und (und haben Sie schon von Triggergenerierung gehört?).

Ein Übergang durch einen verbotenen Zustand erfolgt auch in einem realen Trigger, aber der Erzeugungsmodus ist aufgrund der unterschiedlichen Verzögerungen realer Elemente unmöglich. Feige. Abbildung 6 zeigt den Generierungsmodus des Trigger-Trigger-Modells, der existiert, solange die Einheiten an den Eingängen gespeichert sind.

Bemerkung 2. Eine typische Beschreibung der Funktionsweise des RS-Triggers wird in den allermeisten Fällen in Form einer Wahrheitstabelle gegeben. Wenn Sie jedoch verstehen, dass ein Auslöser ein sequentielles Schema ist, werden diejenigen, die sich mit diesem Thema befassen, absichtlich irregeführt. Nun, kein Auslöser kann keine "verbotenen Zustände" haben! Aber aus irgendeinem Grund beschließen nur wenige, diese Wahrheit zu entdecken und insbesondere das Problem ihrer Entstehung zu diskutieren (siehe zum Beispiel [7]).


Feige. Fig. 7 zeigt das Umschalten eines Triggermodells zwischen seinen stabilen Zuständen. Hier behält ein einzelner Zustand der Triggereingänge den aktuellen Zustand der Triggereingänge bei, und wenn dieser oder jener Eingang auf Null gesetzt wird, wechselt er in den entgegengesetzten Zustand. Zur gleichen Zeit, wenn der Trigger geschaltet wird, nehmen seine Ausgänge im Moment, die einer diskreten Maßnahme entsprechen, gleichzeitig einen einzelnen (von wem verbotenen?) Zustand an.

Bild
Feige. 6. RS-Trigger-Generierungsmodus

Bild
Abb. 7. RS-Trigger zwischen Zuständen umschalten

Betrachten Sie ein anderes RS-Trigger-Modell, das aus einem Zustand und einer Aktion besteht, d. H. ähnlich dem Modell in Listing 1. Sein Code wird in Listing 4 gezeigt. Dieses Modell hat wie das Generatormodell keine Prädikate und die Signalwerte ohne Zwischentransformationen werden in die Aktion y1 eingegeben. Ist das gut oder schlecht? Einerseits scheint es gut zu sein, weil Der Code ist einfacher geworden, aber andererseits ... nicht wirklich. Und wir werden jetzt die Gründe dafür verstehen.

Listing 4. NAND-Elementmodell aus einer Aktion
#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(); }


Wenn wir das neue Modell im "Schattenspeicher" -Modus testen, werden wir keine Unterschiede in seiner Funktionsweise gegenüber dem vorherigen feststellen, d. H. und beim Umschalten durchläuft es die verbotenen Zustände und wechselt regelmäßig in den Generierungsmodus. Wenn wir die Arbeit mit Daten im üblichen Modus einrichten, erhalten wir die in Abb. 1 gezeigten Ergebnisse. 8 und Abb. 9.

Bild
Abb. 8. Ausfall des Generierungsmodus des RS-Trigger-Modells

Bild
. 9. Verbotene Zustände durch das RS-Trigger-

Modell überspringen Warum zeigt das erste Modell, unabhängig von der Art der Arbeit mit dem Speicher, stabile Ergebnisse und das zweite - ändert das Verhalten? Der Grund sind Prädikate. Das zweite Modell hat keine Prädikate, und dies ist entscheidend für sein Verhalten. Aber wie und warum wirkt sich das Vorhandensein / Fehlen von Prädikaten auf den Algorithmus für den parallelen Programmbetrieb aus?

Das Programmmodell eines AND-NOT-Elements hat wie ein Automatprogramm zwei Eingangskanäle und einen Ausgangskanal. Sie müssen zwei Prädikaten und einer Aktion entsprechen. Das erste Programm stimmt voll und ganz damit überein. Der VKPa-Kernel, der die Automatenbeschreibung interpretiert, führt zunächst alle Prädikate nicht nur eines bestimmten Automaten, sondern auch des gesamten Automatenraums aus und startet dann alle Aktionen. In diesem Fall hängen die Ergebnisse von Prädikaten für den aktuellen Taktzyklus des Automaten nicht von ihnen ab (Aktionen), unabhängig davon, in welcher Reihenfolge die Aktionen ausgeführt wurden, um Parallelität zu simulieren und in welchem ​​Modus sie mit dem Speicher arbeiteten. Daher erzeugt das erste Programm das gleiche Ergebnis.

Das zweite Programm liest, obwohl es direkt mit den Eingangskanälen der Maschine arbeitet, die Eingangssignale als Teil der Aktion. Aktionen, die mit Eingabedaten im Schattenspeichermodus arbeiten, schreiben neue Werte in den Schattenspeicher und arbeiten dabei mit Daten, die zu Beginn eines diskreten Taktzyklus gültig sind. Im üblichen Modus „erfassen“ sie die zum Zeitpunkt ihrer Änderung festgelegten Momentanwerte, und somit wird der Algorithmus von den Momenten der Speicheränderung abhängig. Eine ähnliche Abhängigkeit zeigt das zweite Programm. Und selbst wenn Prädikatenmethoden in das zweite Modell eingeführt würden, hätte dies keine Auswirkungen auf die Ergebnisse seiner Arbeit. Entscheidend ist hier nicht die Existenz von Prädikatenmethoden, sondern die Merkmale ihrer Arbeit im Rahmen des Automatenprogrammiermodells.

5. Schlussfolgerungen


Am Beispiel des parallelen RS-Trigger-Programms haben wir einige der Eigenschaften eines parallelen Programms untersucht. Wir werden weiterhin bestimmte allgemeine Aspekte der Funktionsweise paralleler Programme als Beispiel für logische (digitale) Schaltungen betrachten. Die Wahl des Themas der Modellierung digitaler Schaltungen ist hier kein Zufall. Tatsächlich repräsentieren sie in „verfeinerter Form“ die Arbeit paralleler Prozesse. Dies ermöglicht die Analyse der Nuancen von Parallelität, Rasse, Synchronisation, Sackgassen usw. usw. transparent, klar und einfach.

Unabhängig davon, wie Sie die Programmierung als "wettbewerbsfähig" oder parallel bezeichnen, unabhängig davon, ob Sie "Coroutinen", Coroutinen, Threads oder Maschinen zum Programmieren verwenden, sollte das Ergebnis des [parallelen] Programms in allen Implementierungen gleich sein. Das automatische Modell paralleler Programme im Rahmen der KPdSU verfolgt dieses und nur dieses Ziel.
Unabhängig davon, welche Annahmen hinsichtlich der Implementierung des Kerns der Interpretation der Automaten der VKPa-Umgebung getroffen würden, wären dies alles „Spekulationen“, weil Das Ergebnis der Arbeit automatischer Programme sollte nicht mit der Implementierung eines Rechenmodells in Verbindung gebracht werden. Es kann Software (wie es jetzt ist) oder Hardware (wie ich in Zukunft hoffe) sein, die auf einem Kern oder auf ihrem Set implementiert ist, in Single-Threaded- oder Multi-Threaded-Version usw. usw. All dies sollte die Ergebnisse paralleler automatischer Programme in keiner Weise beeinflussen.

Und anscheinend wurde das Ziel erreicht. Das RS-Trigger-Modell als einer der möglichen Tests von Parallelitätssystemen [8] überzeugt uns davon ... Wie das Leben gezeigt hat, funktionieren alle anderen parallelen Programme, sofern die Umgebung die Implementierung des RS-Trigger-Testparallelismus erfolgreich bestanden hat, genauso korrekt, zuverlässig und stabil . Übrigens hat der gleiche MATLAB „RS-Trigger-Test“ nicht bestanden, und das sagt schon viel aus ...

Literatur
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