Ich habe eine Aufgabe, die eine Liste von Dateien auf stdin verarbeitet. Die Startzeit des Programms ist beträchtlich und die Zeit, die jede Datei benötigt, ist sehr unterschiedlich. Ich möchte eine beträchtliche Anzahl dieser Prozesse erzeugen und dann die Arbeit an diejenigen senden, die nicht beschäftigt sind. Es gibt verschiedene Kommandozeilen-Tools, die beinahe das tun, was ich will. Ich habe sie auf zwei beinahe funktionierende Optionen eingegrenzt:
find . -type f | split -n r/24 -u --filter="myjob"
find . -type f | parallel --pipe -u -l 1 myjob
Das Problem ist, dass split
ein reines Round-Robin-Verfahren durchgeführt wird, sodass einer der Prozesse zurückbleibt und den Abschluss der gesamten Operation verzögert. while parallel
möchte einen Prozess pro N Zeilen oder Bytes an Eingaben erzeugen, und ich verbringe viel zu viel Zeit mit dem Start-Overhead.
Gibt es so etwas, das die Prozesse und Zuleitungen zu den Prozessen wiederverwendet, bei denen die Blockierung aufgehoben wurde?
quelle
split
Befehl? Der Name steht in Konflikt mit dem Standarddienstprogramm für die Textverarbeitung.myjob
bereit ist, mehr Input zu erhalten. Es gibt keine Möglichkeit zu wissen, dass ein Programm bereit ist, mehr Eingaben zu verarbeiten. Sie können nur wissen, dass ein Puffer (ein Pipe-Puffer, ein Stdio-Puffer) bereit ist, mehr Eingaben zu empfangen. Können Sie veranlassen, dass Ihr Programm eine Anfrage sendet (z. B. eine Eingabeaufforderung anzeigt), wenn es fertig ist?read
Aufrufe reagiert, den Trick ausführen. Das ist ein ziemlich großer Programmieraufwand.-l 1
in denparallel
Args? IIRC, das parallel anweist, eine Eingabezeile pro Job zu verarbeiten (dh einen Dateinamen pro Fork von myjob, also viel Start-Overhead).Antworten:
Das scheint in einem so allgemeinen Fall nicht möglich zu sein. Es bedeutet, dass Sie für jeden Prozess einen Puffer haben und die Puffer von außen beobachten können, um zu entscheiden, wo der nächste Eintrag abgelegt werden soll (Planung). Natürlich können Sie etwas schreiben (oder ein Batch-System wie slurm verwenden)
Je nachdem, um welchen Prozess es sich handelt, können Sie die Eingabe möglicherweise vorverarbeiten. Wenn Sie zum Beispiel Dateien herunterladen, Einträge aus einer Datenbank aktualisieren oder Ähnliches, aber 50% davon übersprungen werden (und Sie haben daher einen großen Verarbeitungsunterschied, der von der Eingabe abhängt), richten Sie einfach einen Vorprozessor ein Damit wird überprüft, welche Einträge lange dauern werden (Datei vorhanden, Daten wurden geändert usw.), sodass alles, was von der anderen Seite kommt, garantiert eine relativ lange Zeit in Anspruch nimmt. Auch wenn die Heuristik nicht perfekt ist, kann dies zu einer erheblichen Verbesserung führen. Sie können die anderen in eine Datei sichern und anschließend auf die gleiche Weise verarbeiten.
Das hängt jedoch von Ihrem Anwendungsfall ab.
quelle
Nein, es gibt keine generische Lösung. Ihr Dispatcher muss wissen, wann jedes Programm bereit ist, eine andere Zeile zu lesen, und es gibt keinen mir bekannten Standard, der dies zulässt. Alles, was Sie tun können, ist, eine Zeile auf STDOUT zu setzen und darauf zu warten, dass etwas davon verbraucht wird. Es gibt keine gute Möglichkeit für den Produzenten in einer Pipeline zu erkennen, ob der nächste Verbraucher bereit ist oder nicht.
quelle
Ich glaube nicht. In meinem Lieblingsmagazin war einmal ein Artikel über Bash-Programmierung, der tat, was Sie wollten. Ich bin gewillt zu glauben, dass sie diese Werkzeuge erwähnt hätten, wenn es sie gegeben hätte. Sie möchten also etwas im Sinne von:
Natürlich können Sie den Aufruf des aktuellen Skripts nach Ihren Wünschen ändern. Die Zeitschrift, die ich erwähne, macht anfangs Dinge wie das Einrichten von Pipes und das eigentliche Starten von Worker-Threads. Schauen Sie sich
mkfifo
das an, aber diese Route ist weitaus komplizierter, da die Worker-Prozesse dem Master-Prozess signalisieren müssen, dass sie bereit sind, weitere Daten zu empfangen. Sie benötigen also ein FIFO für jeden Worker-Prozess, um die Daten zu senden, und ein FIFO für den Master-Prozess, um die Daten der Worker zu empfangen.HAFTUNGSAUSSCHLUSS Ich habe das Drehbuch von oben geschrieben. Möglicherweise gibt es einige Syntaxprobleme.
quelle
find . -type f | while read i
anstatt zu verwendenfor i in $(find . -type f)
.Für GNU Parallel können Sie die Blockgröße mit --block einstellen. Es ist jedoch erforderlich, dass Sie über genügend Speicher verfügen, um für jeden der ausgeführten Prozesse einen Block im Speicher zu behalten.
Ich verstehe, dass dies nicht genau das ist, wonach Sie suchen, aber es könnte vorerst eine akzeptable Lösung sein.
Wenn Ihre Aufgaben im Durchschnitt dieselbe Zeit in Anspruch nehmen, können Sie möglicherweise mbuffer verwenden:
quelle
Versuche dies:
mkfifo
für jeden Prozess.Dann hängen Sie
tail -f | myjob
an jedem FIFO.Zum Beispiel das Einrichten der Arbeiter (Myjob-Prozesse)
Abhängig von Ihrer Anwendung (myjob) können Sie Jobs verwenden, um angehaltene Jobs zu finden. Andernfalls listen Sie die Prozesse nach CPU sortiert auf und wählen Sie denjenigen aus, der am wenigsten Ressourcen verbraucht. Von sich aus muss der Job gemeldet werden, zB indem im Dateisystem ein Flag gesetzt wird, wenn mehr Arbeit gewünscht wird.
Unter der Annahme, dass der Job beim Warten auf eine Eingabe stoppt, verwenden Sie
jobs -sl
Zum Beispiel, um die PID eines gestoppten Jobs herauszufinden und ihm eine Arbeit zuzuweisenIch habe das mit getestet
Dies muss ich zugeben, wurde nur so ymmv zusammengebraut.
quelle
Was wirklich benötigt wird, um dies zu lösen, ist ein Warteschlangenmechanismus irgendeiner Art.
Ist es möglich, dass die Jobs ihre Eingaben aus einer Warteschlange lesen, z. B. einer SYSV-Nachrichtenwarteschlange, und die Programme dann parallel ausgeführt werden, indem die Werte einfach in die Warteschlange verschoben werden?
Eine andere Möglichkeit ist die Verwendung eines Verzeichnisses für die Warteschlange:
pending
mv
der ersten Dateien aus, die er im Verzeichnis sieht, und zwar in ein gleichrangiges Verzeichnis mit dempending
Nameninprogress
.pending
quelle
Wenn Sie die Antwort von @ ash erläutern, können Sie eine SYSV-Nachrichtenwarteschlange verwenden, um die Arbeit zu verteilen. Wenn Sie kein eigenes Programm in C schreiben möchten, gibt es ein Hilfsprogramm, das Ihnen
ipcmd
helfen kann. Folgendes habe ich zusammengestellt, um die Ausgabefind $DIRECTORY -type f
an die$PARALLEL
Anzahl der Prozesse zu übergeben:Hier ist ein Testlauf:
quelle
Wenn Sie nicht abschätzen können, wie lange eine bestimmte Eingabedatei verarbeitet wird und die Worker-Prozesse keine Möglichkeit haben, dem Scheduler Bericht zu erstatten (wie in normalen Parallel-Computing-Szenarien - häufig über MPI ), haben Sie im Allgemeinen Pech - entweder die Strafe dafür zahlen, dass einige Mitarbeiter Eingaben länger verarbeiten als andere (wegen der Ungleichheit der Eingaben), oder die Strafe dafür zahlen, dass für jede Eingabedatei ein neuer Prozess erstellt wird.
quelle
GNU Parallel hat sich in den letzten 7 Jahren verändert. So kann es heute sein:
Dieses Beispiel zeigt, dass Prozess 11 und 10 mehr Blöcke erhalten als Prozess 4 und 5, da 4 und 5 langsamer lesen:
quelle