Socket, system und Prozess in den Hintergrund schicken
coolblue
- perl
Hallo,
weder fork noch exec scheinen mir bei meinem Problem weiter zu helfen, wenn doch, dann habe ich etwas nicht verstanden oder völlig falsch gemacht.
Ich habe einen Server, der über Socket auf eingehende Verbindungen lauscht. Hierfür benutze ich das Perl-Modul IO::Socket::INET. Mein Client sendet "Befehle" oder auch Pfad+Programmname von Prozessen, die nonstop laufen, an den Server. Der Server soll den Befehl oder den Prozess ausführen und danach weiterhin auf den Port lauschen, um gegebenfalls weitere "Befehle" auszuführen. Er soll nicht auf das Ende des Prozesses warten. Das Problem ist nun, dass der Server an der Stelle, an der er den system Befehl ausführt, solange hängt, bis der mit system gestartete Prozess gekillt wird oder sich selbst beendet.
Ich habe schon viele mögliche Varianten ausprobiert und mich über fork im Archiv kundig gemacht, doch keiner meiner Tests war erfolgreich.
while($client = $server->accept()) {
$command=<$client>;
***system Befehl***
}
An der Stelle von ***system Befehl*** habe ich folgende Tests durchgeführt:
system("$command &");
system("$command 1>/dev/null 2>&1 &");
system('$command &');
system('$command 1>/dev/null 2>&1 &');
#------- fork Test -------#
use POSIX;
while($client = $server->accept()) {
$command=<$client>;
$pid = fork;
exit if $pid;
system("$command &");
}
Diese vorgehensweise funktioniert auch nicht, denn der Serverprozess, ob nun Child oder Parent, wartet trotzdem auf das Ende des Prozesses.
Mein Ziel ist, dass der Prozess, der mit $command ausgeführt wird, im Hintergrund abläuft und der Serverprozess weiterhin auf Verbindungen lauschen kann. Dabei möchte ich nicht, dass der Serverprozess dupliziert wird, denn wenn 100 Prozesse gestartet werden sollen, gibt es doch auch den Serverprozess 100 Mal. Das möchte ich vermeiden. Der Prozess in $command soll losgelöst laufen.
Ausserdem ist der Port dann dauernt von xxx anderen Serverprozessen belegt.
Das Problem scheint sichtlich am Serverprozess zu liegen, denn wenn ich eine ganz normale for Schleife in einem Perl-Script verwende, gibt es keine Probleme.
Hier ein simples Beispiel:
#!/usr/bin/perl
for($ct=0 ; $ct <= 10 ; $ct++) {
print "$ct";
system("sar -u 1 100 &");
}
exit;
Die Ausgabe ist wie folgt:
012345678910
Wenn ich dann in die Prozesstabelle schaue, ist der Prozess "sar" dort zu finden. Das Beispiel hing nicht am system Befehl fest. Warum klappt das nicht über den Serverprozess, der den Befehl vom Client erhält?
Gruß,
coolblue
Hallo,
nun bin ich einen Schritt weiter gekommen.
Das klapp nicht:
$command="sar -u 1 100";
while($client = $server-accept()) {
$command=<$client>;
system("$command &");
}
Das auch nicht:
$command="sar -u 1 100";
while($client = $server-accept()) {
$command=<$client>;
system('$command &');
}
Aber das klappt:
while($client = $server-accept()) {
system('sar -u 1 100 &');
}
Der Serverprozess läuft sauber weiter und lauscht auf weitere Verbindungen. Aber ich möchte gerne einen Skalar benutzen...
Gruß,
coolblue
Halihallo coolblue
$command="sar -u 1 100";
while($client = $server-accept()) {
$command=<$client>;
system("$command &");
}
Achtung: Deine vorherige Zuweisung an $command wird überschrieben.
Vielleicht hast du das nicht gesehen.
Achtung II: <>-Operator gibt auch den Zeilenumbruch aus! - Also:
$command=chomp(<$client>)
system("$command &");
while($client = $server-accept()) {
$command=<$client>;
system('$command &');
}
Natürlich nicht '$command &' ist auch kein Befehl. Nur bei doppelten
Quotes wird die Variable durch dessen Inhalt ersetzt. Vorsicht...
Aber das klappt:
while($client = $server-accept()) {
system('sar -u 1 100 &');
}
Glaube ich nicht. $server-accept() gibt wohl einen Fehler, es muss
$server->accept() heissen :-)
Viele Grüsse
Philipp
Hiho Phillip,
sorry, dass war alles kein cut and paste... sondern habs auf die Schnelle mit einigen Fehlern reingehackt. Nochmal:
$command="sar -u 1 100";
while($client = $server-accept()) {
$command=<$client>;
system("$command &");
}Achtung: Deine vorherige Zuweisung an $command wird überschrieben.
Vielleicht hast du das nicht gesehen.
Achtung II: <>-Operator gibt auch den Zeilenumbruch aus! - Also:
vom Client wird das Kommando sar -u 1 100 gesendet...
Warum funktioniert das nicht:
while($client = $server->accept()) {
$command=<$client>;
system("$command &");
}
das auch nicht:
while($client = $server->accept()) {
system("sar -u 1 100 &");
}
und das doch:
while($client = $server-accept()) {
system('sar -u 1 100 &');
}
Den festen Wert habe ich einfach mal zum Testen reingeschrieben.
Bei den ersten Beiden Beispielen bleibt das Script am system Befehl hängen, bis sar beendet wurde. Beim letzten nicht :(
Gruß,
coolblue
Halihallo coolblue
Warum funktioniert das nicht:
while($client = $server->accept()) {
$command=<$client>;
system("$command &");
}
Weil $command noch ein Leerzeichen enthält und weil & vielleicht
nicht so funktioniert, wie du denkst. Ich bin mir nicht sicher, ob
dies den Prozess detached, ich glaube sogar eher nicht.
das auch nicht:
while($client = $server->accept()) {
system("sar -u 1 100 &");
}
und das doch:
while($client = $server-accept()) {
system('sar -u 1 100 &');
}
Naja, ist doch beides dasselbe, bis auf den -> Operator, den du noch
immer nicht hast und den doublequotes die hier keinen Einfluss haben.
Vielleicht funktioniert es nicht, weil du gar keine Requests auf
deinen Server ausführst...?
Bei den ersten Beiden Beispielen bleibt das Script am system Befehl hängen, bis sar beendet wurde. Beim letzten nicht :(
Wie auch immer, lies auch mein zweites Posting:
https://forum.selfhtml.org/?t=95797&m=581583, das wird ggf. einige Fragen klären.
Viele Grüsse
Philipp
Hallo Phillip,
entschuldige bitte meine Unfähigkeit, die Sache richtig zu schildern.
Ich starte den Serverprozess folgendermaßen:
./slistener.pl
while($client = $server->accept()) {
$command=chomp(<$client>);
print "vor\n";
system("............");
print "nach\n";
}
Daran erkenne ich, ob etwas angekommen ist oder nicht. "vor" wird ausgegeben, "nach" nicht.
Und bitte, korrigiere nicht meine Schreib- oder Syntaxfehler, du weißt was ich meine... :-)
Gruß,
coolblue
Halihallo coolblue
Hallo Phillip,
Wie war das noch mit den Schreib- und Syntaxfehlern? - Ja, ja, ich
weiss ja was du meinst, "Philipp" nämlich, richtig? :-)
So und jetzt lass ich es auch, da du mich so freundlich darum
gebeten hast :-)
entschuldige bitte meine Unfähigkeit, die Sache richtig zu schildern.
Ich glaube ich habe es ja jetzt verstanden ;-)
Zudem brauche auch ich ein Weekend...
Ich starte den Serverprozess folgendermaßen:
while($client = $server->accept()) {
$command=chomp(<$client>);
print "vor\n";
system("............");
print "nach\n";
}Daran erkenne ich, ob etwas angekommen ist oder nicht. "vor" wird ausgegeben, "nach" nicht.
Huh? - Das wäre dann so ein Programm, das immer läuft, nicht? Denn
system sollte schon irgendwann aufhören...
Bezüglich anderem Posting:
Super, dann tut's ja jetzt. Wegen chomp:
my $command = <$client>;
$command=chomp($command);
oder
my $command = chomp($client->getline());
Viele Grüsse
Philipp
Hallo PHILIPP :-)
my $command = <$client>;
$command=chomp($command);
Warum unbedingt chomp? Dieses Beispiel habe ich bisher oft in Foren und Beispielen geselesen, aber geht es denn auch nicht so:
my $command=<$client>;
$command=~s/\n//;
Was wäre daran falsch? Zuviel Code? Zu umständlich - ist ja auch nur eine Zeile mehr (!) ? Oder mehr Rechenzeit - kann unter umständen wichtig sein (!) ?
Gruß,
coolblue
Halihallo coolblue
Hallo PHILIPP :-)
:-)
my $command = <$client>;
$command=chomp($command);Warum unbedingt chomp?
chomp ist der adäquate Befehl um den Delimiter (im Normalfall die
Newline) abzuschneiden. Adäquat deswegen weil Newline nicht umbedingt
der Delimiter für die IO-Operationen sein muss.
Genausogut könntest du z.B. folgendes tun:
use IO::String; # IO::Socket::INET ist nicht grossartig anders...
$/ = ';'; # INPUT_RECORD_SEPERATOR / Delimiter
my $s = IO::String->new('1;2;3;4');
# ^^^^^^^^ das wären dann deine $comment's halt
# eben mit ; getrennt statt mit Newlines
while ( my $rec = $s->getline() ) { # getline trennt jetzt bei ';'
my $rec2 = chomp($rec);
print "$rec ist '$rec', wohingegen $rec2 '$rec2' ist.\n";
}
Was wird bei den $rec2's ausgegeben? - Die ';' sind weg, weil chomp
eben auf $/ Rücksicht nimmt. Vielleicht entscheidest du dich
irgendwann deinen Server mit $comment's zu füttern, die über ';'
getrennt sind und nicht mit Newlines. Dann würde ein s/\n$//
jämmerlich versagen, da es eben nicht adäquat für die gewünschte
Funktionsweise ist.
Dieses Beispiel habe ich bisher oft in Foren und Beispielen geselesen, aber geht es denn auch nicht so:
my $command=<$client>;
$command=~s/\n//;
Solange der INPUT_RECORD_SEPERATOR ($/) die Newline ist, ja.
Performanter wäre hier jedoch $command =~ s/\n$//;
Da die Position von \n der RegExp-Engine bereits bekanntgegeben wird.
Was wäre daran falsch?
Bis auf den Umstand dass es performanter geschrieben werden kann und
der INPUT_RECORD_SEPERATOR nicht zwingend \n sein muss, nichts.
Zuviel Code?
Naja, die zwei drei Zeichen mehr...
Zu umständlich
Nicht adäquat würde ich als Grund angeben.
ist ja auch nur eine Zeile mehr (!) ?
Hä?
Oder mehr Rechenzeit - kann unter umständen wichtig sein (!) ?
Puh, das würde nur ein Benchmark beweisen können. Kannst es ja mal
testen:
perldoc Benchmark
Viele Grüsse
Philipp
Hallo,
ich nochmal. Nun ja, da ich auch den Client programmiere und darauf acht, immer ein Newline als Separator mitzugeben, kann doch eigentlich nichts schiefgehen.
Dein Beispiel 1;2;3;4 könnte ich doch auch mit split auseinanderziehen, oder etwa nicht?
Sorry, Perl ist eine neue Welt für mich... lerne auch fleißig :-)
Gruß,
coolblue
Halihallo coolblue
ich nochmal. Nun ja, da ich auch den Client programmiere und darauf acht, immer ein Newline als Separator mitzugeben, kann doch eigentlich nichts schiefgehen.
Ja, natürlich. Mit chomp ist es nur eben wirklich adäquater. Zudem
könnte ich mir vorstellen, dass chomp auch schneller ist, aber das
liesse sich nur durch einen Benchmark belegen.
Dein Beispiel 1;2;3;4 könnte ich doch auch mit split auseinanderziehen, oder etwa nicht?
Natürlich ginge es auch mit split. Nur: splitte mal eine 10GB grosse
Eingabedatei (hier ist es halt eine Variable, aber mit IO::File
kann man das auch mit einer Datei machen), das dauert ein bissle und
konsumiert sau viel Speicher.
Sorry, Perl ist eine neue Welt für mich... lerne auch fleißig :-)
Warum sorry? - Du machst ja alles richtig, immer weiter!
Viele Grüsse
Philipp
Ok, du hast Recht!
Bevor ich ein Programm beginne, mache ich mir natürlich Gedanken, welche Funktionen das Programm erfüllen soll und wo die Grenzen liegen. Da ich niemals ein 10GB File auseinanderziehen muss, sondern eben immer nur einen Befehl ausführen möchte, reicht das kleine Serverprogramm vollkommen aus.
Aber ich werde deinen Rat befolgen und in all meinen Scripts ein Chomp einbauen!
_Vielen_ _Dank_ für deinen Rat!
Hier noch zwei weitere Threads von mir, bei denen ich mich auf deine Meinung freuen würde!
http://forum.de.selfhtml.org/my/?t=95810&m=581630
http://forum.de.selfhtml.org/my/?t=95804&m=581603
Gruß,
coolblue
Halihallo coolblue
Bevor ich ein Programm beginne, mache ich mir natürlich Gedanken, welche Funktionen das Programm erfüllen soll und wo die Grenzen liegen. Da ich niemals ein 10GB File auseinanderziehen muss, sondern eben immer nur einen Befehl ausführen möchte, reicht das kleine Serverprogramm vollkommen aus.
Natürlich, oftmals ist es aber so, dass die Anforderungen an ein
Programm mit der Zeit ändern, also ist man angehalten möglichst schon
am Anfang "gut" (was immer das heissen mag) zu programmieren (es sei
denn der Aufwand übersteigt den (späteren) Nutzen nachweisbar).
Aber ich werde deinen Rat befolgen und in all meinen Scripts ein Chomp einbauen!
_Vielen_ _Dank_ für deinen Rat!
Aber gerne.
Hier noch zwei weitere Threads von mir, bei denen ich mich auf deine Meinung freuen würde!
Keine Sorge, ich lese viel im Forum und wenn ich antworten möchte,
dann tue ich es auch ohne Hinweis darauf :-)
Viele Grüsse
Philipp
Hallo Philipp,
$command="sar -u 1 100\n";
$command=chomp($command);
print "$command\n";
Ausgabe ist: 1
???
Viele Grüße,
coolblue
Halihallo coolblue
$command="sar -u 1 100\n";
$command=chomp($command);
print "$command\n";
Ausgabe ist: 1
Ups, mein Fehler. chomp gibt die Anzahl gelöschter Zeichen zurück,
nicht der modifizierte String. Wenn du chomp($command) schreibst, hat
$command nachher kein abschliessende Newline mehr (ohne erneute
Zuweisung zu $command!).
Also:
$command="sar -u 1 100\n";
chomp($command);
print "$command\n";
Viele Grüsse
Philipp
Hiho,
Also:
$command="sar -u 1 100\n";
chomp($command);
print "$command\n";
ok, funzt!
Viele Grüße,
coolblue
my $command = <$client>;
$command=chomp($command);Warum unbedingt chomp? Dieses Beispiel habe ich bisher oft in Foren und Beispielen geselesen, aber geht es denn auch nicht so:
my $command=<$client>;
$command=~s/\n//;
Welchen sinn soll es machen eine funktion die vorhanden ist und auch mehreren Umständen Funktioniert gegen eine langsamere und schlecht zu wartende Funktion zu ersetzen?
Du willst nicht schnell an's Ziel kommen sondern möglichst umständlich?
Struppi.
Hallo Struppi,
Welchen sinn soll es machen eine funktion die vorhanden ist und auch mehreren Umständen Funktioniert gegen eine langsamere und schlecht zu wartende Funktion zu ersetzen?
$command=chomp($command);
$command=~s/\n//;
Du willst nicht schnell an's Ziel kommen sondern möglichst umständlich?
Danke für das hilfreiche Kommentar.
Gruß,
coolblue
Hallo coolblue,
"chomp($command)" entfernt nur den letzten Zeilenumbruch,
"$command=~s/\n//" dagegen lässt alle Zeilenumbrüche in $command verschwinden.
Hallo coolblue,
"chomp($command)" entfernt nur den letzten Zeilenumbruch,
"$command=~s/\n//" dagegen lässt alle Zeilenumbrüche in $command verschwinden.
nun da es in meiner Struktur, in der ich Daten über die Socketverbindung übertrage, nur einen Zeilenumbruch gibt, löst diese Art des entfernens auch mein Problem. Nur hätte ich gerne gewußt, ob diese Art tatsächlich so inperformant ist, wie Struppi behauptete.
Gruß,
coolblue
nun da es in meiner Struktur, in der ich Daten über die Socketverbindung übertrage, nur einen Zeilenumbruch gibt, löst diese Art des entfernens auch mein Problem. Nur hätte ich gerne gewußt, ob diese Art tatsächlich so inperformant ist, wie Struppi behauptete.
Ich bin nicht gut was Benchmarks angeht ( das Modul wurde dir ja schon nahegelegt):
use Benchmark;
my $text = 'test\n';
Benchmark::cmpthese(-1, {
'regExp$' => sub { $text =~ s/\n$//; },
'regExp' => sub { $text =~ s/\n//; },
'chomp' => sub { chomp $text; },
});
Benchmark: running chomp, regExp, regExp$, each for at least 3 CPU seconds...
chomp: 3 wallclock secs ( 3.22 usr + 0.00 sys = 3.22 CPU) @ 19810824.17/
s (n=63771043)
regExp: 2 wallclock secs ( 3.14 usr + 0.00 sys = 3.14 CPU) @ 8796678.66/s
(n=27621571)
regExp$: 3 wallclock secs ( 3.06 usr + 0.00 sys = 3.06 CPU) @ 13534063.03/
s (n=41441301)
Rate regExp regExp$ chomp
regExp 8796679/s -- -35% -56%
regExp$ 13534063/s 54% -- -32%
chomp 19810824/s 125% 46% --
aber wie immer, es kann sein das der Benchmark nicht realisitisch ist, da ich nicht mit Mechanismen vertraut bin.
Struppi.
Hallo Struppi,
das find ich ja klasse!
use Benchmark;
my $text = 'test\n';
Benchmark::cmpthese(-1, {
'regExp$' => sub { $text =~ s/\n$//; },
'regExp' => sub { $text =~ s/\n//; },
'chomp' => sub { chomp $text; },
});Benchmark: running chomp, regExp, regExp$, each for at least 3 CPU seconds...
chomp: 3 wallclock secs ( 3.22 usr + 0.00 sys = 3.22 CPU) @ 19810824.17/
s (n=63771043)
regExp: 2 wallclock secs ( 3.14 usr + 0.00 sys = 3.14 CPU) @ 8796678.66/s
(n=27621571)
regExp$: 3 wallclock secs ( 3.06 usr + 0.00 sys = 3.06 CPU) @ 13534063.03/
s (n=41441301)
Rate regExp regExp$ chomp
regExp 8796679/s -- -35% -56%
regExp$ 13534063/s 54% -- -32%
chomp 19810824/s 125% 46% --
Aber widerlegt das nicht deine Theorie, das chomp schneller ist als mein Vorgehen? Aus dem Ergebnis erkenne ich, dass chomp 0.06 CPU-Sekunden mehr benötigt. Ich neige wohl jetzt eher dazu, dass Beispiel von Philipp einzusetzen (s/\n$//).
Ich bitte um Antwort.
aber wie immer, es kann sein das der Benchmark nicht realisitisch ist, da ich nicht mit Mechanismen vertraut bin.
Benchmark ist nicht irgend ein "Tool". Die Tests mit Benchmark sind sehr nahe an der Realität! Ein absolut professionelles Auswertungstool. Wenn "wir" neue Großrechner einkaufen, egal ob bei IBM, Sun oder HP, werden die Maschinen ersteinmal mit einem Großteil unserer eingesetzten Software, zum Beispiel Oracle und selbstentwickelter Jobs, ausführlich mit Benchmark darauf getestet.
Selbst zu ext2, ext3, reiser etc. machen wir Tests mit Benchmark.
Viele Grüße,
coolblue
Halihallo coolblue
Benchmark: running chomp, regExp, regExp$, each for at least 3 CPU seconds...
chomp: 3 wallclock secs ( 3.22 usr + 0.00 sys = 3.22 CPU) @ 19810824.17/
s (n=63771043)
regExp: 2 wallclock secs ( 3.14 usr + 0.00 sys = 3.14 CPU) @ 8796678.66/s
(n=27621571)
regExp$: 3 wallclock secs ( 3.06 usr + 0.00 sys = 3.06 CPU) @ 13534063.03/
s (n=41441301)
Rate regExp regExp$ chomp
regExp 8796679/s -- -35% -56%
regExp$ 13534063/s 54% -- -32%
chomp 19810824/s 125% 46% --Aber widerlegt das nicht deine Theorie, das chomp schneller ist als mein Vorgehen? Aus dem Ergebnis erkenne ich, dass chomp 0.06 CPU-Sekunden mehr benötigt. Ich neige wohl jetzt eher dazu, dass Beispiel von Philipp einzusetzen (s/\n$//).
Achtung, es werden *rund* 3 CPU-Sekunden gemessen. *rund* kann
systembedingt eben genau mal 0.06 CPU-Sekunden mehr sein, bzw. im
Benchmark von Stuppi sogar 0.22. Aber wichtig ist nicht die
verbrauchte Zeit, sondern die Anzahl Iterationen pro Sekunde. Wie
du in Stuppis Bench lesen kannst, ist comp mit 20 mio/s eindeutig
am schnellsten. Bei meinem Benchmark hat chomp etwa zweimal mehr
Strings bearbeiten können, als die beiden RegExp-Lösungen. Meiner
Meinung nach ein signifikantes Ergebnis. chomp ist eindeutig der
Champion :-)
aber wie immer, es kann sein das der Benchmark nicht realisitisch ist, da ich nicht mit Mechanismen vertraut bin.
Benchmark ist nicht irgend ein "Tool". Die Tests mit Benchmark sind sehr nahe an der Realität! Ein absolut professionelles Auswertungstool. Wenn "wir" neue Großrechner einkaufen, egal ob bei IBM, Sun oder HP, werden die Maschinen ersteinmal mit einem Großteil unserer eingesetzten Software, zum Beispiel Oracle und selbstentwickelter Jobs, ausführlich mit Benchmark darauf getestet.
Nun, Benchmarks sind allgemein keine einfache Sache. Die Ergebnisse
sind oft nicht so eindeutig wie man sie gerne hätte. Oftmals spielen
bei simpelsten Tests ganz andere Faktoren auch hinein, die eben auch
beachtet werden müssen und die Folge ist: selbst einfache Benchmarks
können hochkomplex werden bzw. liefern falsche Aussagen. Ich könnte
mir vorstellen, dass Stuppi in etwa dies sagen wollte.
Viele Grüsse
Philipp
Nun, Benchmarks sind allgemein keine einfache Sache. Die Ergebnisse
sind oft nicht so eindeutig wie man sie gerne hätte. Oftmals spielen
bei simpelsten Tests ganz andere Faktoren auch hinein, die eben auch
beachtet werden müssen und die Folge ist: selbst einfache Benchmarks
können hochkomplex werden bzw. liefern falsche Aussagen. Ich könnte
mir vorstellen, dass Stuppi in etwa dies sagen wollte.
Das sehe ich ein, aber trotzallem muss man sich auf etwas stützen können. Natürlich wird versucht, bei solchen Tests alle möglichen und vor allem alle wissentlichen Faktoren einzukalkulieren.
Bei solchen Benchmarks werden für gewöhnlich keine einzelnen Statements geprüft, sondern viel mehr ganze Applikationen und Jobs, die seit eh und jeh eingesetzt werden.
Aber trotzdem finde ich die Ausführung von Struppi genial :-)
Viele Grüße,
coolblue
Hallo Philipp,
Achtung, es werden *rund* 3 CPU-Sekunden gemessen. *rund* kann
systembedingt eben genau mal 0.06 CPU-Sekunden mehr sein, bzw. im
Benchmark von Stuppi sogar 0.22. Aber wichtig ist nicht die
verbrauchte Zeit, sondern die Anzahl Iterationen pro Sekunde. Wie
du in Stuppis Bench lesen kannst, ist comp mit 20 mio/s eindeutig
am schnellsten. Bei meinem Benchmark hat chomp etwa zweimal mehr
Strings bearbeiten können, als die beiden RegExp-Lösungen. Meiner
Meinung nach ein signifikantes Ergebnis. chomp ist eindeutig der
Champion :-)
für mein technisches Verständnis... damit ich das nicht falsch verstehe...
Der Benchmarktest lief für jedes Statement ca. 3 CPU-Sekunden. Innerhalb dieser Zeit wurde für jedes Statement die Iteration gemessen.
Ist das so richtig? Ansonsten versuche es mir verständlich zu erklären, wenn du dir die Zeit dafür nehmen magst.
Viele Grüße,
coolblue
Halihallo coolblue
für mein technisches Verständnis... damit ich das nicht falsch verstehe...
perldoc Benchmark
oder
http://www.perldoc.com/perl5.8.4/lib/Benchmark.html
Der Benchmarktest lief für jedes Statement ca. 3 CPU-Sekunden. Innerhalb dieser Zeit wurde für jedes Statement die Iteration gemessen.
die Anzahl Iterationen. Eine Iteration ist z.B. genau einmal chomp
auszuführen. Für uns ist nun wichtig zu wissen, wie oft chomp
pro Sekunde ausgeführt werden kann und das ist die
(Anzahl Iterationen)/Sekunde.
Ist das so richtig? Ansonsten versuche es mir verständlich zu erklären, wenn du dir die Zeit dafür nehmen magst.
Ich glaube du hast schon verstanden. Ansonsten rate ich dir
einmal einen Blick in die obengenannte Dokumentation zu werfen, dort
wird alles schön erklärt.
Viele Grüsse
Philipp
Hallo Philipp,
Ich glaube du hast schon verstanden.
jupp :-)
Viele Grüße,
coolblue
Halihallo Struppi
So, jetzt bin ich auch mal grad am Benchmarken gewesen.
use Benchmark;
my $text = 'test\n';
Hast du dir $text mal ausgeben lassen?
my $text = "test\n";
sonst gibt's keine abschliessende Newline und du misst zwar etwas,
aber bestimmt etwas falsches :-)
Benchmark::cmpthese(-1, {
'regExp$' => sub { $text =~ s/\n$//; },
'regExp' => sub { $text =~ s/\n//; },
'chomp' => sub { chomp $text; },
});
Das Problem ist, dass bei $text nur bei der ersten Iteration die
Newline weggehackt wird und alle anderen haben dann nur noch 'test'
zu behandeln. Wir messen also wiederum nicht das, was wir wollen.
Kleines Update zum Benchmark:
use Benchmark;
my $text2 = "befehl -cvzf test parameter\n";
## möglichst Realitätsnahe
my $text = $text2;
Benchmark::cmpthese(2000000, {
'regExp$' => sub { $text=$text2; $text =~ s/\n$//; },
'regExp' => sub { $text=$text2; $text =~ s/\n//; },
'chomp' => sub { $text=$text2; chomp $text; },
});
Nunja, das mit den $text=$text2 ist zwar auch nicht das, was wir
messen wollen, aber ohne messen wir einfach das falsche. Nun, deshalb
habe ich einfach fix 2000000 Iterationen veranschlagt, somit ist
$text=$text2 bei allen drei Tests nahezu Konstant und wir messen nur
die Unterschiede bei der Newline-wegtrennung. Zudem musste ich diesen
Benchmark einige Male nacheinander durchführen um sicher zu gehen,
dass die Ergebnisse immer in etwa dieselben bleiben (da $text=$text2
viel länger dauert als Newlinewegtrennung können die Ergebnisse durch
aus falsch sein, über mehrmaliges Iterieren kann man jedoch bei
fast immerwährend gleichen Ergebnissen eine These formulieren).
Ach ja, man hätte auch "local $text" sagen können. Aber dort haben wir
das Problem, dass local so dermassen langsam ist, dass wir bei allen
drei cmp's so ziemlich denselben Durchsatz haben...
Nunja, unter'm Strich kann ich sagen: Ich komme auf dieselben
Aussagen (und Folgen) wie du :-)
chomp ist am schnellsten, dann kommt regExp$ und dann chomp. Beim
genauen Hinsehen fällt sogar auf, dass chomp stets fast doppelt so
schnell ist wie die beiden RegExpe und die regExp$ nur wenig
schneller ist, als regExp. Wenn man jedoch standardmässig sehr viel
längere Shell-Befehle absetzen möchte (>2000 Zeichen), dann wird's
wohl eine starke Verbesserung von regExp$ bezüglich regExp geben (
müsste man alerdings auch wieder prüfen).
Rate regExp regExp$ chomp
regExp 8796679/s -- -35% -56%
regExp$ 13534063/s 54% -- -32%
chomp 19810824/s 125% 46% --
Ich habe in etwa folgendes:
Rate regExp regExp$ chomp
regExp 1422475/s -- -1% -56%
regExp$ 1437815/s 1% -- -55%
chomp 3205128/s 125% 123% --
Danke für deine Benchmark's Stuppi, hat mich grad motiviert selber
auszuprobieren.
Viele Grüsse
Philipp
Halihallo zusammen
Kleines Update zum Benchmark:
BTW: Stuppi, du misst sozusagen wie schnell die drei Lösungen den
Newline erkennen. IMO auch ein spannendes Ergebnis. chomp erkennt
die Newline am schnellsten (wahrscheinlich deshalb, da es nur einen
gewissen Endstring mit dem INPUT_RECORD_SEPERATOR vergleichen müss),
regExp$ merkt ihn am zweitschnellsten (auch wieder deshalb, da die
Position durch $ bereits manifestiert ist) und regExp muss den ganzen
String nach der ersten Newline durchsuchen.
Man sieht jedoch beim Vergleichen des Benchmarks von Stuppi und mir
auch, dass chomp die Newline anscheinend auch schneller Löschen kann
(sonst wären die Verhältnisse der Durchsätze der beiden Benchmarks
konstanter). Dies lässt mich die These formulieren, dass chomp
einfach die Stringlänge anpasst und die Regular Expression-Lösungen
(auch wenn der Substring ganz am Ende des Strings ist) zeitaufwendige
Stringmanipulationen (String-Replacements) durchführen. Es ist jedoch
nur eine These...
Brave new benchmark-world :-)
BTW: Wenn jemand Lust hat, könnte man nun auch noch ein
=~ s/\n$//o in die Lösungsliste aufnehmen. Hier wird die RegExp
nur einmal kompiliert und sollte demnach nochmals etwas
schneller sein als die anderen beiden RegExp's. Aber chomp wird
wohl stets am schnellsten bleiben.
Viele Grüsse
Philipp
Halihallo zusammen
Kleines Update zum Benchmark:
BTW: Stuppi, du misst sozusagen wie schnell die drei Lösungen den
Newline erkennen.
Erkennen oder auch tatsächlich wegschneiden?
Iteration: wie oft die Newline innerhalb von 3 CPU-Sekunden erkannt wird? und/oder weggeschnitten wird?
Viele Grüße,
coolblue
Halihallo coolblue
BTW: Stuppi, du misst sozusagen wie schnell die drei Lösungen den
Newline erkennen.
Erkennen oder auch tatsächlich wegschneiden?
Bei Stuppi: erkennen. Da stets die Variable $text verwendet wird, ist
diese bereits beim zweiten Durchlauf ohne abschliessende Newline.
Deswegen gibt es für alle folgenden Durchläufe gar nichts mehr
wegzuschneiden, da es gar keine Newlines mehr wegzuschneiden gibt.
Deswegen nur erkennen.
Iteration: wie oft die Newline innerhalb von 3 CPU-Sekunden erkannt wird? und/oder weggeschnitten wird?
Ja. Stuppi's Benchmark läuft ziemlich genau 3 CPU-Sekunden
(theoretisch sogar ganz genau genau) für jede der drei
Lösungsvorschläge (chomp, regExp, regExp$). Nun zählt der Benchmark
wie oft die einzelnen Methoden innerhalb dieser 3 CPU-Sekunden
durchlaufen werden können. Diese Durchläufe nennt man Iterationen.
Aufgrund der Anzahl an Iterationen und der genauen Zeitmessung kann
berechnet werden, wieviele Iterationen pro Sekunde möglich sind und
das ist genau die Grösse, die wir wissen wollen, denn daran kann man
ablesen welche Lösung nun die bessere ist.
Viele Grüsse
Philipp
Hallo Philipp,
das gefällt mir! Genau die richtige Art Langläufer zu Tunen.
Sowas habe ich gebraucht.
Viele Grüße,
coolblue
So, jetzt bin ich auch mal grad am Benchmarken gewesen.
use Benchmark;
my $text = 'test\n';Hast du dir $text mal ausgeben lassen?
my $text = "test\n";
sonst gibt's keine abschliessende Newline und du misst zwar etwas,
aber bestimmt etwas falsches :-)
Jaja, hatt ich auch schon daran gedacht (deshalb meine Aussage mit dem Mechanismus, mir schwante sowas bereits).
Das Problem, wie auch schon angedeutet hast, ist (vermutlich) dass die Zuweisung eines neuen Strings natürlich auch Zeit in anspruch nimm und wir das ja nicht messen wollen.
Benchmark::cmpthese(-1, {
'regExp$' => sub { $text =~ s/\n$//; },
'regExp' => sub { $text =~ s/\n//; },
'chomp' => sub { chomp $text; },
});Das Problem ist, dass bei $text nur bei der ersten Iteration die
Newline weggehackt wird und alle anderen haben dann nur noch 'test'
zu behandeln. Wir messen also wiederum nicht das, was wir wollen.Kleines Update zum Benchmark:
use Benchmark;
my $text2 = "befehl -cvzf test parameter\n";
## möglichst Realitätsnahe
my $text = $text2;Benchmark::cmpthese(2000000, {
'regExp$' => sub { $text=$text2; $text =~ s/\n$//; },
'regExp' => sub { $text=$text2; $text =~ s/\n//; },
'chomp' => sub { $text=$text2; chomp $text; },
});Nunja, das mit den $text=$text2 ist zwar auch nicht das, was wir
messen wollen, aber ohne messen wir einfach das falsche. Nun, deshalb
habe ich einfach fix 2000000 Iterationen veranschlagt, somit ist
$text=$text2 bei allen drei Tests nahezu Konstant und wir messen nur
die Unterschiede bei der Newline-wegtrennung. Zudem musste ich diesen
Benchmark einige Male nacheinander durchführen um sicher zu gehen,
dass die Ergebnisse immer in etwa dieselben bleiben (da $text=$text2
viel länger dauert als Newlinewegtrennung können die Ergebnisse durch
aus falsch sein, über mehrmaliges Iterieren kann man jedoch bei
fast immerwährend gleichen Ergebnissen eine These formulieren).
jaja nicht einfach das Benchmarking. Ich wollte mich auch zuerst davor drücken (war ja auch schon spät) aber letztlich siegte meine persönliche Neugier und auch wenn collblue ein bisschen dogmatisch rüberkommt, scheint er ja nicht total ignorant zu sein. Insofern hoffte ich einerseits auf Verbesserungen von Anderen an meinem Code (was passiert ist) und das ich coolblue zumindest einen Ansatz zeigen konnte mit dem er was anfangen kann (was ja auch passiert ist).
Es hat sich also für alle gelohnt ;-)
Danke für deine Benchmark's Stuppi, hat mich grad motiviert selber
auszuprobieren.
gern geschehen :-)
Struppi.
Halihallo EisFux
"chomp($command)" entfernt nur den letzten Zeilenumbruch,
"$command=~s/\n//" dagegen lässt alle Zeilenumbrüche in $command verschwinden.
Nö, das wäre $command =~ s/\n//g;
=~ s/\n//; entfernt nur einen Zeilenumbruch und das ist - wie
coolblue - richtig feststellt genau der Letzte.
Aber wie ich bereits sagte, =~ s/\n$//; ist noch performanter, da der
Zeilenumbruch nicht erst gefunden werden muss, sondern nur das letzte
Zeichen mit \n verglichen werden muss und falls es denn gleich ist,
wird es "weg-replaced".
Viele Grüsse
Philipp
Hallo Philipp,
Nö, das wäre $command =~ s/\n//g;
das ist echt fieß von dir! Ich dachte schon daran, ihn darauf aufmerksam zu machen, weil es mir auch aufgefallen war, habe aber den Mund gehalten, weil ich kürzlich dich noch darum bat, sowas nicht zu tun...
tz tz tz :-)
Gruß,
coolblue
Halihallo und guten Morgen coolblue
Nö, das wäre $command =~ s/\n//g;
das ist echt fieß von dir! Ich dachte schon daran, ihn darauf aufmerksam zu machen, weil es mir auch aufgefallen war, habe aber den Mund gehalten, weil ich kürzlich dich noch darum bat, sowas nicht zu tun...
Och, wer ein klein wenig konstruktive Kritik nicht verträgt, sollte
besser zu Hause im Bett bleiben :-) (das sage ich jetzt weder zu
dir, noch zu EisFux)
Mal im Ernst, diese kleinen Fehler werden ins Archiv verschoben und
ggf. von Suchenden gefunden. Sie lesen, dass s/\n//; alle Zeilenumbrüche
löscht und versuchen das in ihren Programmen. Das funktioniert dann
nicht und schon haben wir einen neuen Thread mit neuer Frage im
Forum, und das nur, weil ein kleiner Fehler nicht gleich da
korrigiert wurde, wo er entstanden ist. Du kannst dir vorstellen,
dass dies im Worst-Case eine exponenzielle Kettenreaktion hervorrufen
kann :-)
Natürlich soll man jetzt auch nicht hingehen und jeden noch so
kleinen Fehler korrigieren, das würde am Ziel auch
vorbeischiessen. Die Entscheidung was korrigiert werden soll, muss
jeder Poster für sich selber entscheiden. Aber *das* einige Fehler
korrigiert werden obwohl jeder (oder eben *fast* jeder) den Inhalt
verstanden hat, kann manchmal wirklich richtig sein.
Nun ja, von Zeit zu Zeit entscheide auch ich mich dazu, ob hier der
richtige Zeitpunkt war, darf natürlich angezweifelt werden :-)
tz tz tz :-)
Ach, geh schlafen! :-)
Viele Grüsse
Philipp
Moin moin Philipp,
Mal im Ernst, diese kleinen Fehler werden ins Archiv verschoben und
ggf. von Suchenden gefunden. Sie lesen, dass s/\n//; alle Zeilenumbrüche
löscht und versuchen das in ihren Programmen. Gruß,
von dieser Seite hatte ich das bisher noch nicht betrachtet! Ich werde mir das zu Herzen nehmen und Fehler Anderer, wenn ich dann mal online bin und welche finde... kritisieren, aber freundlichst.
Aber zunächst sollte ich dann wohl bei mir selbst anfangen :-)
Bis dann...
coolblue
Halihallo Philipp,
Mal im Ernst, diese kleinen Fehler werden ins Archiv verschoben und
ggf. von Suchenden gefunden. Sie lesen, dass s/\n//; alle Zeilenumbrüche
löscht und versuchen das in ihren Programmen. Das funktioniert dann
nicht und schon haben wir einen neuen Thread mit neuer Frage im
Forum, und das nur, weil ein kleiner Fehler nicht gleich da
korrigiert wurde, wo er entstanden ist. Du kannst dir vorstellen,
dass dies im Worst-Case eine exponenzielle Kettenreaktion hervorrufen
kann :-)
Ich hab mir schon mehrmals eine Funktion im Forum gewünscht, mit der ich ein schon abgeschicktes Posting nachträglich korrigieren kann. Die Vorschaufunktion hilft bei solchen Schusselfehlern leider nicht :(
Nun ja, von Zeit zu Zeit entscheide auch ich mich dazu, ob hier der
richtige Zeitpunkt war, darf natürlich angezweifelt werden :-)
War er. Ich hätte es sonst nicht gemerkt. Aber ich entferne die (letzten) Zeilenumbrüche (in einem String) sonst auch immer mit chomp(). ;-)
Viele Grüsse
Dir auch.
你好 EisFux,
Ich hab mir schon mehrmals eine Funktion im Forum gewünscht, mit der ich
ein schon abgeschicktes Posting nachträglich korrigieren kann. Die
Vorschaufunktion hilft bei solchen Schusselfehlern leider nicht :(
Wird es nicht geben, kann es auch nicht geben. Gruende finden sich im
Archiv.
再见,
CK
Hallo coolblue,
Nö, das wäre $command =~ s/\n//g;
das ist echt fieß von dir! Ich dachte schon daran, ihn darauf aufmerksam zu machen, weil es mir auch aufgefallen war, habe aber den Mund gehalten, weil ich kürzlich dich noch darum bat, sowas nicht zu tun...
Hättst du es mal getan. Kritik ist nicht fies, sondern notwendig. Wer nicht ab und zu (berechtigt) kritisiert wird, hält sich irgendwann für den größten Superhelden. Ab und zu verbal ein paar hinter die Löffel bringen einen wieder auf den Boden der Tatsachen zurück. Und wer nichts macht, macht auch keine Fehler.
Gruß,
EisFuX
Halihallo Philipp Hasenfratz,
Nö, das wäre $command =~ s/\n//g;
Ja richtig, ich habe das kleine g verschusselt. Kein Wunder bei der Uhrzeit ...
=~ s/\n//; entfernt nur einen Zeilenumbruch und das ist - wie
coolblue - richtig feststellt genau der Letzte.
Ähm, wenn ich mal vorsichtig berichtigen dürfte:
Nein, es entfernt den ersten, der im String auftaucht, chomp() dagegen den letzen. Aber das nur so nebenbei.
Im Übrigen ist das Verhalten von chomp() stark von der Variablen $/ abhängig. Es hackt nicht immer den letzen Zeilenumbruch weg. Wobei Zeilenumbruch ja auch nur eine ungefähre Umschreibung von Ausdrücken wie "\n" oder "$/" ist. ;-)
Aber wie ich bereits sagte, =~ s/\n$//; ist noch performanter, da der
Zeilenumbruch nicht erst gefunden werden muss, sondern nur das letzte
Zeichen mit \n verglichen werden muss und falls es denn gleich ist,
wird es "weg-replaced".
Nur mal interessehalber: Macht sich der Performanceunterschied auch in normalen Webanwendungen bemerkbar (Ausführungszeit, Speicherverbrauch)? Da vertrödelt ein Perl-Skript die meiste Zeit doch sowieso damit, auf den Datenbankserver zu warten ...
Viele Grüsse
Genau, die vergess ich immer am Schluss.
Halihallo EisFux
=~ s/\n//; entfernt nur einen Zeilenumbruch und das ist - wie
coolblue - richtig feststellt genau der Letzte.Ähm, wenn ich mal vorsichtig berichtigen dürfte:
Nein, es entfernt den ersten, der im String auftaucht, chomp() dagegen den letzen. Aber das nur so nebenbei.
Richtig.
Im Übrigen ist das Verhalten von chomp() stark von der Variablen $/ abhängig. Es hackt nicht immer den letzen Zeilenumbruch weg. Wobei Zeilenumbruch ja auch nur eine ungefähre Umschreibung von Ausdrücken wie "\n" oder "$/" ist. ;-)
Genau. Deswegen sollte man ja chomp() verwenden, sonst müsste man
jedesmal wenn die Definition von "Zeilenumbruch" ändert, die RegExp
anpassen.
Aber wie ich bereits sagte, =~ s/\n$//; ist noch performanter, da der
Zeilenumbruch nicht erst gefunden werden muss, sondern nur das letzte
Zeichen mit \n verglichen werden muss und falls es denn gleich ist,
wird es "weg-replaced".Nur mal interessehalber: Macht sich der Performanceunterschied auch in normalen Webanwendungen bemerkbar (Ausführungszeit, Speicherverbrauch)? Da vertrödelt ein Perl-Skript die meiste Zeit doch sowieso damit, auf den Datenbankserver zu warten ...
Du hast dir die Frage bereits beantwortet: Der Performanceunterschied
ist verschwindenst klein. Du sparst bei 3.2 Mio. Scriptaufrufen
ziemlich genau nur 1 CPU-Sekunde :-)
Viele Grüsse
Philipp
Halihallo
Du hast dir die Frage bereits beantwortet: Der Performanceunterschied
ist verschwindenst klein. Du sparst bei 3.2 Mio. Scriptaufrufen
ziemlich genau nur 1 CPU-Sekunde :-)
Äm, auch meinem Computer zumindest (Pentium 4 mit 2.25 GHz)... Wenn
schon genau berechnen, dann auch halbwegs genau beschreiben...
Viele Grüsse
Philipp
Halihallo coolblue
weder fork noch exec scheinen mir bei meinem Problem weiter zu helfen, wenn doch, dann habe ich etwas nicht verstanden oder völlig falsch gemacht.
Es wäre gut einen Link auf vorherige Postings zu haben, wo das Thema
anscheinend schon erörtert wurde.
Der Server soll den Befehl oder den Prozess ausführen und danach weiterhin auf den Port lauschen, um gegebenfalls weitere "Befehle" auszuführen. Er soll nicht auf das Ende des Prozesses warten. Das Problem ist nun, dass der Server an der Stelle, an der er den system Befehl ausführt, solange hängt, bis der mit system gestartete Prozess gekillt wird oder sich selbst beendet.
Das wiederspiegelt sich in deinen Programmen. Ich komme gleich
dazu...
while($client = $server->accept()) {
$command=<$client>;
***system Befehl***
}
Hier ist wohl klar, dass er immer auf Beendigung von system() wartet,
da kein fork verwendet wird; system wartet immer auf das Beenden des
Prozesses, auch wenn da ein & steht...
system('$command &');
Nicht probieren, wissen. Variablen werden nur in Doublequotes
aufgelöst...
use POSIX;
wofür?
while($client = $server->accept()) {
$command=<$client>;
$pid = fork;
exit if $pid;
system("$command &");
}
Also:
Server lauscht...
Server erhält connection -> $client
*Server* wird beendet (was er nicht soll!).
Client führt $command aus und lauscht dann weiter.
Das wird besser so gemacht:
while ( $client = $server->accept()) {
$command = chomp(<$client>);
$pid = fork();
die('cannot fork!') unless (defined($pid));
unless ($pid) { # also Child...
system("$command"); # ... führt Befehl aus...
exit; # ... und stirbt dann.
}
# und Parent lauscht gleich weiter!
}
So, damit sollte es dann funktionieren.
Viele Grüsse
Philipp
Supi, danke für deine Hilfe, aber
» Das wird besser so gemacht:
»
» while ( $client = $server->accept()) {
» $command = chomp(<$client>);
» $pid = fork();
» die('cannot fork!') unless (defined($pid));
» unless ($pid) { # also Child...
» system("$command"); # ... führt Befehl aus...
» exit; # ... und stirbt dann.
» }
» # und Parent lauscht gleich weiter!
» }
»
» So, damit sollte es dann funktionieren.
Leider nicht so wie ich es gemeint hatte. Der Child-Prozess wartet solange, bis der $command Prozess beendet ist. Folgendes Szenario:
Ich sende 100 mal den Befehl "sar -u 1 100" an den Server. Dann habe ich 100 Mal den sar Befehl gestartet => korrekt. Aber dann habe ich auch 100 Child Prozesse, die auf das Ende ihrer sar Prozesse warten. Nun gibt es allerdings Prozesse die eventuell gestartet werden, welche nonstop laufen. Ich möchte nicht, dass die Child Prozesse existieren. Ich möchte den $command Befehl absetzen, ohne das ein Child Prozess bis zum bitteren Ende existiert.
Gruß,
coolblue
Halihallo coolblue
Ich sende 100 mal den Befehl "sar -u 1 100" an den Server. Dann habe ich 100 Mal den sar Befehl gestartet => korrekt. Aber dann habe ich auch 100 Child Prozesse, die auf das Ende ihrer sar Prozesse warten. Nun gibt es allerdings Prozesse die eventuell gestartet werden, welche nonstop laufen. Ich möchte nicht, dass die Child Prozesse existieren. Ich möchte den $command Befehl absetzen, ohne das ein Child Prozess bis zum bitteren Ende existiert.
Ach so ist das... Also:
use POSIX qw(setsid);
while ( $client = $server->accept()) {
$command = chomp(<$client>);
$pid = fork();
die('cannot fork!') unless (defined($pid));
unless ($pid) { # also Child...
setsid(); # ... wird vollkommen von Parent getrennt
exec("$command"); # ... und mit $command Befehl "ersetzt"
}
# und Parent lauscht gleich weiter!
}
Viele Grüsse
Philipp
Hola, et funzt...
»
» Ach so ist das... Also:
»
» use POSIX qw(setsid);
»
» while ( $client = $server->accept()) {
» $command = chomp(<$client>);
» $pid = fork();
» die('cannot fork!') unless (defined($pid));
» unless ($pid) { # also Child...
» setsid(); # ... wird vollkommen von Parent getrennt
» exec("$command"); # ... und mit $command Befehl "ersetzt"
» }
» # und Parent lauscht gleich weiter!
» }
»
» Viele Grüsse
»
» Philipp
Nur eine Sache noch, dein chomp funktioniert nicht.
Meldung: cant modify handle in chomp ... (<$client>)
Gruß,
coolblue