Multicore-optimierte Shell-Skripte

Kamikaze

Warrior of Sunlight
Teammitglied
Ich habe gerade mein Skript pkg_libchk parallelisiert. Auf meinem Dual-Core bringt das 31,309% Laufzeitverkürzung. Im Moment teste ich, ob es Performancekosten auf einem Single-Core System mit sich bringt. Falls nicht, wird das auch so in meinen Port einfließen.

Ein paar Erfahrungen, die ich gemacht habe:

Statusausgaben werden nur von einem Prozess neu gezeichnet. Den Bedarf melden die Kindprozesse über ein Signal. Ich SIGCHLD verwendet.

Man benötigt auf jeden Fall eine Trap für SIGTERM und SIGINT um Kindprozesse zu töten, wenn die Ausführung gestoppt wird.

Es scheint sich zu lohnen 2 Jobs pro Kern zu spendieren.

Die Zahl der geforkten Prozesse prüfe ich mit 100Hz. Bei 10Hz zum Beispiel frisst die Wartezeit, die Prozesse später als nötig geforkt werden, einen signifikanten Teil des Laufzeitgewinns. Vor allem wenn man nur einen Job pro Kern verwendet. Ich werde damit noch experimentieren.

Update:

Wenn das neue Skript in einen 1-Job-Modus gezwungen wird, ist es über 16% langsamer als das Original. Dabei handelt es sich also um Overhead, der mit mehr Prozessen durch die Nutzung mehrerer Kerne ausgeglichen werden muss. Das bestätigt sich auch auf meinem Single-Core P4 mit 1.6 GHz. Dort ist das parallelisierte Skript satte 19% langsamer.

Jetzt lautet die Frage was mehr wiegt. 19% langsamer bei Single-Core oder 30% schneller bei Dual-Core?
 
Zuletzt bearbeitet:
Da ich keine Doppelher(t)z-CPUs habe, würde ich für den Single Core votieren. Wäre aber mal interessant, (per Umfrage?) herauszufinden, wie groß der Verbreitungsgrad von Doppelkern-CPUs bei FreeBSD-Nutzern mittlerweile ist. In ein paar Jahren dürfte sich die Frage wahrscheinlich gar nicht mehr stellen...
 
Ich habe gerade mein Skript pkg_libchk parallelisiert. Auf meinem Dual-Core bringt das 31,309% Laufzeitverkürzung. Im Moment teste ich, ob es Performancekosten auf einem Single-Core System mit sich bringt. Falls nicht, wird das auch so in meinen Port einfließen.

Ein paar Erfahrungen, die ich gemacht habe:

Statusausgaben werden nur von einem Prozess neu gezeichnet. Den Bedarf melden die Kindprozesse über ein Signal. Ich SIGCHLD verwendet.

Man benötigt auf jeden Fall eine Trap für SIGTERM und SIGINT um Kindprozesse zu töten, wenn die Ausführung gestoppt wird.

Es scheint sich zu lohnen 2 Jobs pro Kern zu spendieren.

Die Zahl der geforkten Prozesse prüfe ich mit 100Hz. Bei 10Hz zum Beispiel frisst die Wartezeit, die Prozesse später als nötig geforkt werden, einen signifikanten Teil des Laufzeitgewinns. Vor allem wenn man nur einen Job pro Kern verwendet. Ich werde damit noch experimentieren.

Update:

Wenn das neue Skript in einen 1-Job-Modus gezwungen wird, ist es über 16% langsamer als das Original. Dabei handelt es sich also um Overhead, der mit mehr Prozessen durch die Nutzung mehrerer Kerne ausgeglichen werden muss. Das bestätigt sich auch auf meinem Single-Core P4 mit 1.6 GHz. Dort ist das parallelisierte Skript satte 19% langsamer.

Jetzt lautet die Frage was mehr wiegt. 19% langsamer bei Single-Core oder 30% schneller bei Dual-Core?
Ich hab hier ein Singlecore und zusätzlich keine Ahnung vom scripten. Könntest du aber nicht die Anzahl der Cores abfragen und dementsprechende Variante laufen lassen? Wenn es nicht geht, wären mir die 19% langsamer egal. Wenn das Programm seine Sache tut, ist mir das auch langsamer recht. ;)
 
Das mit den Cores abfragen mache ich schon.

Inzwischen habe ich die Single-Core Einbuße auf 8.5% gesenkt. Der Vorteil bei meinem Dual-Core ist dafür auf 35.6% angestiegen.

Die Einbuße bei Single-Core Systemen kommt daher, dass durch die Fähigkeit zu parallelisieren ein weiterer Prozess nötig wird, der die Prozesse, die die eigentliche Arbeit machen, überwacht.

Ich habe mich jetzt auf 2 Prozesse pro Kern festgelegt, so verringert sich die wahrscheinlichkeit, dass ein Kern arbeitslos wird auch wenn der Überwachungsprozess länger schläft. Ich werde am Schlafinterval weiterschrauben, vielleicht lässt sich da ja noch der ein- oder andere Prozentpunkt herausholen.
 
Mir ist ein echter Durchbruch gelungen!

Ich benutze Locking auf temporären Dateien um einen Semaphor zu simulieren. Damit habe ich die Performance-Einbuße auf dem Single-Core System unter die 1.5% Marke gedrückt, während ich auf dem Dual-Core eine Laufzeit-Verkürzung um 36% verbuchen kann.

Ich denke damit ist klar, dass ich das Skript mit dem nächsten Release umstelle. Es wird noch ein Tick verlangsamt, weil ich noch Locking für die Statusmeldungen implementieren muss, damit es nicht Artefakten bei der Ausgabe kommen kann.
 
Echt ma! Dann wird man ja sicher mit einem Extrakapitel im Script-HowTo rechnen können :)
 
@Steve
Ich lass es nach jedem Update der Ports laufen. Erspart einem das portupgrade -rf wenn sich mal wieder Libraryversionen geändert haben.

@Elwood
Vielleicht landet das auch irgendwann im HowTo, aber erst einmal sollte ich den Einstieg seichter gestalten. Für das Locking habe ich 8 kleine aber nicht gerade intuitiv nachvollziehbare Funktionen geschrieben.
Die müssen korrekt verwendet werden, sonst bleibt das Skript irgendwann stehen oder frisst CPU ohne etwas zu tun. Und wenn man dann noch was mit den Signalen falsch macht fängt es an ziemlich schlecht reproduzierbare Core-Dumps zu produzieren oder man kann das Skript nur noch mit einem killall -9 sh stoppen. Ich weiß nicht, ob das der richtige Stoff für jemanden ist, der gerade den Einstieg in Shell-Skripte sucht.

@Marz
Wie MrFixit so hilfreich vorgeführt hat, bin ich bei weitem nicht allwissend. ;)

@Ararat++
Es landet garantiert nicht im Papierkorb. Aber ich will es noch eine Weile testen, bevor ich es auf die Öffentlichkeit loslasse.

Update:
Die Laufzeitveränderungen in der Finalen Version (wenn ich keine Fehler mehr entdecke) sind jetzt folgendermaßen:
Single-Core: + 3.5%
Dual-Core: - 35.0%

Zum Forken neuer Prozesse wartet das Skript auf die Freigabe durch einen Semaphor. Zugriff auf die Statuszeile erfolgt per exklusivem Lock. Beides missbraucht das Programm lockf um zu verhindern, dass sich verschiedene Prozesse gegenseitig in die Quere kommen.
 
Zuletzt bearbeitet:
Eigentlich dachte ich, dass nichts peinlicher als ein Kleinwagen Tuning (Sportauspuff, Tieferlegung usw.) ist.

Dieser Thread hat mich eines Besseren belehrt, Du Shell-Skript-Gott.
 
Für mich ist das die beste Methode abzuschalten (neben Fahrrad-fahren). Es gibt einfach Leute die an allem Hand anlegen. Meine make-Konfiguration ist auch über 200 Zeilen lang. Und meinem Fahrrad habe ich Stahl-ummantelte Bremsschläuche spendiert.
 
sehr leiwand, man könnte versuchen das ganze schellskript parallelisiert von clusterrechner
in aller Welt abarbeiten zu lassen.stell dir vor.
 
Das mit dem abschalten hab ich auch. Ich hab die Tage zum Spass ein Skript zum DVD nach x264 geschrieben, mit allem pipapo. Es gibt nichts schöneres als das wenn es funktioniert und man an allen Ecken und Kanten noch was verbessern kann. Da gibts einen schönen Satz zu: The beauty of code
Und Deinen muss ich mir unbedingt angucken!
 
Fein, du hast ja die neue Version bereits fertig :)

Eine kleine Anmerkung habe ich "aber" dazu... Durch das Abarbeiten mehrerer jobs gleichzeitig kommt es insbesondere bei pkg_libchk zu einer Ausgabe, die "durcheinander" ist. Ich habe aktuell sehr viel fehlende libs, da ich von 6.3 auf 7.0 aktualisiert habe. pkg_libchk bringt zB ständig abwechselnd de-openoffice.org-2.3.1 und dann diablo-jdk und dann mal wieder openoffice usw. :)

Gruß
 
Ja ich weiß, stört dich das? Ich empfand das nicht als Problem, wenn du das anders siehst, bau ich einen Flag ein, der die Ausgaben sammelt und zusammen abgibt. Dann bekommst du den Output aber nicht mehr in Echtzeit sondern immer erst, wenn ein Job fertig ist.
 
Moin,

"stören" ist etwas anderes. Ich bin froh über die Arbeit, die das Programm macht! :) Aber wenn du mich so fragst: Ja, ein sortierter Output wäre nett. Ich habe schon angefangen, den Output in eine Datei umzuleiten (pkg_libchk > /foo/bar) aber wirklich brauchbar ist das Ergebnis nicht. Aktuell unterbreche ich alle Nase lang den pkg_libchk Prozess, baue die Pakete die aufgelistet werden neu und lasse pkg_libchk dann erneut laufen. So komm ich zumindest langsam voran.

Warum ich das mache? Ich will ein portupgrade -af umgehen... :)

Auf einer 6.3-Maschine habe ich den Port auch neu gebaut. Ich würde sogar behaupten, dass das Script selbst auf der Singlecore maschine schneller ist, was aber auch täuschen kann, da ich keine Tests gemacht habe :)

Gruß
 
Wenn du den Output in eine Datei umleitest ist -c obligatorisch. Du kannst auch -q nehmen, das ist schneller und impliziert -c. Ich persönlich benutze für deinen Anwendungsfall -qo. Bedenke aber, dass JDKs und OpenOffice immer auftauchen werden.
 
Oh, sorry. Ich hatte mir extra vorgenommen zu schreiben, dass ich die manpage in diesem Punkt noch (!) nicht zu rate gezogen habe. Ich bin nämlich (wirklich) davon ausgegangen, dass es dort erwähnt wird :)
 
Wenn du den Output in eine Datei umleitest ist -c obligatorisch. Du kannst auch -q nehmen, das ist schneller und impliziert -c. Ich persönlich benutze für deinen Anwendungsfall -qo. Bedenke aber, dass JDKs und OpenOffice immer auftauchen werden.
Ja, genau diese Pakete hast du ja auch in der manpage genannt. :)

Wenn ich pkg_libchk -q > /foo/bar aufrufe, werden ALLE Pakete gelistet und nicht nur die "defekten"... pkg_libchk -c > /foo/bar scheint dagegen zu funktionieren (läuft aber noch).

Gruß
 
Kann es sein, dass du gerade ein Upgrade von 6x auf 7x hinter dir hast und einiges mit compat-libraries verlinkt ist? Dann sollten da schon ein paar Hundert Pakete rauskommen. Allerdings nicht alle.

Bei mir erzeugt das Kommando folgende Ausgabe:
> pkg_libchk -q
diablo-jdk-1.5.0.07.01_9
en-openoffice.org-GB-2.3.1_1
jdk-1.6.0.3p4

Wenn dir sortierter Output wichtig ist, kannst du auch -j1 setzen bis es die neue Version gibt, dann wird das Skript allerdings langsamer. Ich kann mir nicht vorstellen, warum -q nicht funktionieren sollte.
 
Moin,

klar habe ich ein Upgrade von 6x auf 7x hinter mir (siehe oben ;) ). pkg_libchk -q > /foo/bar bringt wirklich ALLE, auch die, die ich bereits bereinigt habe. pkg_libchk -c > /foo/bar bringt nur die "defekten". Zwar mehrfach aber immerhin.

Ich kann dir gerne mal die Dateien zukommen lassen, die ich erzeuge (also einmal mit -q und einmal mit -c).

Gruß
 
Zurück
Oben