Inhalt

  • Vier Kerne, aber ...
  • Synchronisierte Schnittstellen
  • Farbbefüllung von Bildern
  • Aufgabe erfüllt!
  • Keine fraktalen Farbverläufe
  • Es funktioniert? Aber ...
  • Zum Weitermachen
  • Das perfekte Werkzeug
  • Das gute, alte Buch


Hinter­grund

Vier Kerne, aber ...

Mein kleiner Sony-Rechner mit seinem 13-Zoll-Bild­schirm zu­sam­men mit dem 14 Jah­re alten 22-Zoll-Röh­ren­mo­ni­tor sind eine fei­ne Sa­che. Die CPU hat vier Re­chen­ker­ne - wenn aber ein Pro­gramm un­ter Voll­dampf werkelt, zeigt der Win­dows-Pro­zeß­ma­na­ger oft nur ei­ne CPU-­Aus­las­tung von 25% an. Drei Kerne tun (fast) nichts ...

Und die Herren Burns und Wellings stellen in ih­rem Buch (Ka­pi­tel 11.10) con­cur­rent exe­cu­tion ab­strac­tions ... for crea­ting con­cur­rent exe­cu­tion en­gi­nes vor. Rich­tig, ein Gerüst, ei­ne Rah­men­kon­struk­tion muss her, um eine ge­eig­ne­te Auf­ga­be mit leich­ter Hand auf mehrere Kerne ver­tei­len zu kön­nen.

Das Rad soll ja im Projekt nicht immer wieder neu er­fun­den werden - auch ich will es nicht neu (und schlech­ter) er­fin­den. Ich ha­be den Ada-Co­de al­so denn doch und wohl oder übel (1) eifrig ab­ge­tippt, mir eine ba­na­le An­wen­dung aus der Bild­ver­ar­bei­tung aus­ge­dacht - und das Gan­ze auch an­stän­dig zum Laufen ge­bracht ...

(1) Den Quellcode gab es einmal auf den Webseiten der Au­to­ren und der war leider un­voll­ständig und fast un­brauch­bar.



Kon­zepte

Typerweiterungen und synchronisierte Schnittstellen

Die prächtige Programmiersprache Ada kennt seit Ada'95 Typ­er­wei­te­run­gen mit­tels tagged types und seit Ada'05 solche Schnittstellen wie sie auch Ja­va anbietet und darüber hinausgehend die syn­chro­ni­sier­ten Schnitt­stel­len.

Diese Schnittstellen a'la Java erlauben eine ge­zähm­te Form der mehr­fa­chen Ver­er­bung: Ein ab­ge­lei­tevter Typ kann also Ei­gen­schaf­ten und Funk­tio­nen seines Obertypen erben, man kann aber zu­sätz­lich be­schrei­ben, dass Objekte des Typs zum Beispiel auch druck­bar oder aus­führ­bar sein sollen. Dazu müssen die Objekte ent­spre­chen­de rein funktionale Schnitt­stel­len im­ple­men­tiert ha­ben.

Hier soll nun ein Objekt etwas verrichten, etwas tun - dazu muss das Ob­jekt aufgerufen wer­den kön­nen, es muss aufrufbar sein! Klar, das Objekt ist eben eine Routine und muss die fol­gen­de Schnitt­stel­le er­fül­len:

Cal­lab­les

Die abstrakte Schnittstelle für aufrufbare Dinge

Die Zeile 10 definiert den Typen Callable_Type als Schnitt­stel­le. Ein Ob­jekt C dieses Typs muss auf­ge­ru­fen werden können (Zeilen 13, 14) und zwar mit einem (komplexen) Ein­gangs­pa­ra­me­ter P und ei­nem Er­geb­nis vom Typ Result_Type. Ein­gangs- und Er­geb­nis­pa­ra­me­ter werden als ge­ne­ri­sche Typen (Zeilen 5, 6) bereit­ge­stellt.

Der Aufruf des Objektes liefert ein Ergebnis und das braucht ein Plätz­chen, wo es abgelegt und spä­ter auch abgeholt werden kann, man braucht ei­nen Platz­halter, ein future. Solche futures fin­den sich et­wa auch in der funk­tio­na­len Pro­gram­mier­spra­che Clojure.

Fu­tu­res

Die Schnittstellendefinition für solch einen zu­künf­ti­gen Platz­hal­ter ist:

Das Datum, das Platz finden soll, wird als ge­ne­ri­scher Typ (Zeile 4) über­ge­ben, mittels Set (Zeile 23) und Get (Zeile 20) kann das Da­tum ab­ge­legt und abgeholt werden.

Die Schnittstelle ist synchronized (Zeilen 8, 17), das be­deu­tet, dass sie mittels Tasks oder über ge­schütz­te Typen (pro­tec­ted types) im­ple­men­tiert wervden muss und and Fu­tu­re_Type (Zeile 17) fordert die Implementierung der ent­spre­chen­den Schnitt­stel­le.

Die abstrakte Schnittstelle für einen Platzhalter

Exe­cu­tors

Der Rezeptur fehlt noch ein Wir­kungs­trä­ger, die auf­ruf­ba­re Rou­tine muss ja noch aus­ge­führt wer­den, wir brauchen noch ein Ding mit der Ei­gen­schaft, ausführbar zu sein - und da­hin­ter ver­ste­cken sich na­tür­lich Tasks, die auf die ver­schie­devnen CPU-Kerne verteilt wer­den sol­len! Gran­dios ...

Die abstrakte Schnittstelle für einen Ausführenden

Ein Executor muss natürlich kennen, was er zur Aus­füh­rung brin­gen soll - genau das erfährt er über die generische Schnitt­stel­le, da­run­ter die bei­den Aus­prägungen zweier ge­ne­ri­scher Pakete.

In der weiteren Implementierung wird dann ein Pool von Tasks be­reit­ge­stellt, um die an­ste­hen­de Ar­beit er­le­di­gen zu kön­nen.

Ge­nug da­von!

Das mag als Anregung reichen - mehr finden Sie nur im Quell­code oder im Buche.



An­wen­dung

Farbbefüllung von Bildern

Der kleine Rechner ist schnell, ein ziem­lich gro­ßes Bild im Bit­map-For­mat woll­te ich nach dem Zu­falls­prin­zip mit Farben befüllen. Für die Be­ar­bei­tung wird das Bild in Segmente ein­ge­teilt, für je­des Seg­ment ist ei­ne eigene Task zu­stän­dig und die Tasks werden auf die CPU-­Ker­ne verteilt ...



Test

Aufgabe erfüllt!

Das Testprogramm ist schön kurz und knapp. 16 Seg­men­te, jedes 4096 x 4096 Pixel groß, wa­ren durch 8 Tasks auf 4 Kernen zu befüllen.

Kurz und knapp

Und alle vier Kerne rackern, 100% CPU-Aus­las­tung, man sieht es auch an den grü­nen Kur­ven.

Der Prozeßmanager zeigt es!



Ein Bild

Keine fraktalen Farbverläufe

Am Ende war ich doch neugierig, wie so ein Bild aus­sieht, bei dem die 24-Bit-Farben für je­den Farb­kanal (pseu­do-)zu­fäl­lig er­zeugt wer­den. Ich hat­te - ohne nachgedacht zu haben - ein graues Bild er­war­tet.

Hier das eigens erzeugte 800x­800-Pixel-Bild ver­klei­nert auf 300x300 Pixel. Man er­kennt groß­flä­chi­ge Schlie­ren­muster.

Ungrau in Zufallsfarben

Gimp spuckt im Histogramm-Dialog einige Zah­len zu den Farb­ka­nä­len aus, unter anderem die Mit­tel­wer­te; diese sind für Rot 127,6, für Grün 127,5 und für Blau 127,4.

Re-initialisiert man den Zufallszahlgenerator (mit ei­ner zeit­ab­hän­gi­gen 'Prise'), so erhält man ähn­li­che Schlie­ren, die aber an­ders ver­teilt sind. Nimmt man den­sel­ben Generator für alle drei Farb­ka­nä­le, er­gibt sich ein grö­ber ge­strick­tes Schlie­ren­mus­ter. Ich wüss­te im Au­gen­blick nicht, wie man hier einen ma­the­ma­tisch-sta­tis­ti­schen Zu­gang erhalten könnte. Viel­leicht hat der op­ti­sche Augen­ein­druck ja auch keine Re­le­vanz?

Verknäulte Fäden ('strings') mit IrfanView
jpg-Format, komprimiert (50%)

Der Bildbetrachter IrfanView zeigt bei der höch­sten Ver­grö­ße­rungs­stu­fe kei­ne Pi­xel­qua­dra­te, sondern 'verschmiert' die Far­ben zu ei­nem ver­schlun­ge­nen Knäuel von fadenar­ti­gen Ge­bil­den, ei­ner rech­ten String­suppe. Die jpg-Komprimierung - von 751 KB auf 45 KB! - verstärkt das Schlie­ren­mus­ter et­was. Gezippt oder im png-For­mat bringt die Komprimierung fast nichts ...



Grü­bel

Es funktioniert? Aber ...

Mein Taskpool umfasste am Ende 8 Tasks, bei nur 4 Tasks stieg das Pro­gramm aus. Ein Wett­ren­nen um Res­sour­cen?

AdaCore stellt für WinzigWeich Windows nur den 32-Bit-Com­pi­ler zur Ver­fü­gung. Und auf mei­nem 64-Bit-Rechner funk­tioniert der 32-­bittige De­bug­ger nicht. An­sons­ten hät­te ich wohl nach der Ur­sa­che geforscht :-)

Und: Ich habe versucht, das Bitmap-Datei-Format di­rekt durch ei­nen Ada-Typ und Dar­stel­lungs­klau­seln ab­zubilden - es ist mir nicht ge­lun­gen ... was aber auch nicht so tragisch ist, denn das Spei­cher­for­mat braucht ja nicht iden­tisch mit den Ver­ar­bei­tungs­for­ma­ten zu sein.

Im Bitmap-Format mit 24-Bit-Farben werden die Farb­da­ten zei­len­wei­se ab­ge­legt, wobei auf die Dop­pelvwort­grenvze mit Nullbytes auf­ge­füllt wer­den muss. Der Verbundtyp Row_­Colours_­Record_­Ty­pe (Zeilen 36-38) lei­stet dies etwa für 3 Pi­xel oh­ne wei­te­re (mög­li­che) Vorgaben - was aber ada­tech­nisch nur bei bekannter Pi­xel­zahl so mach­bar ist. Eine wei­ter­ge­hen­de Pa­ra­me­tri­sie­rung schei­ter­te ...

(Man kann dem genannten Verbundtyp na­tür­lich die Pi­xel­zahl über eine Dis­kri­mi­nan­te mitteilen, die wür­de aber auch an dieser Stelle ab­ge­spei­chert wer­den und da gehört sie nicht hin.)

Bitgenaue Positionierung im Speicher mit Darstellungsklauseln



Quell­code

Hinweis: Die Zeilennummern stimmen nicht un­be­dingt mit denen in den obi­gen Code-Aus­schnit­ten überein.

•  Der Quellcode, gezippt    Mein GNAT-Projekt zum Weiter­machen



Ada-Com­piler

•  GNAT-Ada-Compiler        von AdaCore



Lite­ratur

Das gute, alte Buch

John Barnes
Programming in Ada 2005
Addison-Wesley, 2006

Alan Burns, Andy Wellings
Concurrent and Real-Time Programming in Ada 2005
Cambridge University Press, 2007

Dank an die Herren Burns und Wellings
                      - für die Gedankenarbeit!