Für dieses Golfspiel muss eine Fakultätsberechnung auf mehrere Threads oder Prozesse aufgeteilt werden.
Einige Sprachen erleichtern die Koordinierung als andere, daher ist es lang agnostisch. Ungolfed-Beispielcode wird bereitgestellt, Sie sollten jedoch Ihren eigenen Algorithmus entwickeln.
Das Ziel des Wettbewerbs ist es, herauszufinden, wer den kürzesten (in Bytes, nicht in Sekunden) mehrkernigen faktoriellen Algorithmus zur Berechnung von N finden kann! gemessen in Stimmen, wenn der Wettbewerb endet. Es sollte einen Multicore-Vorteil geben, daher wird vorausgesetzt, dass er für N ~ 10.000 funktioniert. Die Wähler sollten abstimmen, wenn der Autor keine gültige Erklärung dafür abgibt, wie er die Arbeit unter den Prozessoren / Kernen verteilt, und auf der Grundlage der Golfpräzision abstimmen.
Bitte geben Sie aus Neugierde einige Leistungszahlen an. Es kann irgendwann zu einem Kompromiss zwischen Leistung und Golf-Score kommen. Spielen Sie Golf, solange dies den Anforderungen entspricht. Ich wäre gespannt, wann dies passiert.
Sie können normal verfügbare Single-Core-Big-Integer-Bibliotheken verwenden. Zum Beispiel wird Perl normalerweise mit bigint installiert. Beachten Sie jedoch, dass das Aufrufen einer vom System bereitgestellten Fakultätsfunktion die Arbeit normalerweise nicht auf mehrere Kerne aufteilt.
Sie müssen den Eingang N von STDIN oder ARGV akzeptieren und den Wert von N! An STDOUT ausgeben. Sie können optional einen zweiten Eingabeparameter verwenden, um auch die Anzahl der Prozessoren / Kerne für das Programm anzugeben, damit es nicht das tut, was Sie unten sehen werden :-) Oder Sie entwerfen explizit für 2, 4, was auch immer verfügbar ist.
Ich werde mein eigenes Oddball-Perl-Beispiel veröffentlichen, das zuvor für Stack Overflow unter Faktorielle Algorithmen in verschiedenen Sprachen eingereicht wurde . Es ist kein Golf. Zahlreiche andere Beispiele wurden eingereicht, viele davon Golf, aber viele nicht. Verwenden Sie den Code in den Beispielen im obigen Link als Ausgangspunkt, da es sich um Lizenzen handelt, die sich an Freigaben orientieren.
Die Leistung in meinem Beispiel ist aus mehreren Gründen mangelhaft: Sie verwendet zu viele Prozesse und zu viel String / Bigint-Konvertierung. Wie gesagt, es ist ein absichtlich komisches Beispiel. Es wird 5000 berechnen! in weniger als 10 Sekunden auf einem 4-Kern-Rechner hier. Ein offensichtlicherer Zwei-Liner für die nächste Schleife kann jedoch 5000 leisten! auf einem der vier Prozessoren in 3.6s.
Du musst es definitiv besser machen:
#!/usr/bin/perl -w
use strict;
use bigint;
die "usage: f.perl N (outputs N!)" unless ($ARGV[0] > 1);
print STDOUT &main::rangeProduct(1,$ARGV[0])."\n";
sub main::rangeProduct {
my($l, $h) = @_;
return $l if ($l==$h);
return $l*$h if ($l==($h-1));
# arghhh - multiplying more than 2 numbers at a time is too much work
# find the midpoint and split the work up :-)
my $m = int(($h+$l)/2);
my $pid = open(my $KID, "-|");
if ($pid){ # parent
my $X = &main::rangeProduct($l,$m);
my $Y = <$KID>;
chomp($Y);
close($KID);
die "kid failed" unless defined $Y;
return $X*$Y;
} else {
# kid
print STDOUT &main::rangeProduct($m+1,$h)."\n";
exit(0);
}
}
Mein Interesse daran ist einfach (1) Langeweile zu lindern; und (2) etwas Neues lernen. Dies ist für mich kein Problem mit Hausaufgaben oder Nachforschungen.
Viel Glück!
Antworten:
Mathematica
Eine parallele Funktion:
Wo g ist
Identity
oderParallelize
je nach Art des Prozesses erforderlichFür den Timing-Test werden wir die Funktion geringfügig modifizieren, sodass die tatsächliche Uhrzeit zurückgegeben wird.
Und wir testen beide Modi (von 10 ^ 5 bis 9 * 10 ^ 5): (nur zwei Kernel hier)
Ergebnis:
quelle
Haskell:
209200198177 Zeichen176167 Quelle +3310 Compiler-FlagDiese Lösung ist ziemlich dumm. Es wendet product parallel zu einem Wert vom Typ an
[[Integer]]
, bei dem die inneren Listen höchstens zwei Elemente lang sind. Sobald die äußere Liste auf höchstens 2 Listen beschränkt ist, reduzieren wir sie und nehmen das Produkt direkt. Und ja, die Typprüfung benötigt einen Integer-Kommentar, sonst wird sie nicht kompiliert.(Fühlen Sie sich frei, den mittleren Teil von
f
zwischenconcat
unds
als "bis ich das Herz der Länge" zu lesen )Die Dinge schienen ziemlich gut zu werden, da parMap von Control.Parallel.Strategies es ziemlich einfach macht, dies auf mehrere Threads zu übertragen. Es sieht jedoch so aus, als ob GHC 7 satte 33 Zeichen in Befehlszeilenoptionen und Umgebungsvariablen erfordert, damit die Thread-Laufzeit tatsächlich mehrere Kerne verwenden kann (was ich in die Gesamtsumme aufgenommen habe).Es sei denn , ich bin etwas fehlt, das istmöglich , aufjeden Fall der Fall . ( Update : Die Thread-GHC-Laufzeit verwendet offenbar N-1-Threads, wobei N die Anzahl der Kerne ist, sodass Sie nicht mit den Laufzeitoptionen herumspielen müssen.)Kompilieren:
Die Laufzeit war jedoch ziemlich gut, wenn man bedenkt, dass eine lächerliche Anzahl paralleler Auswertungen ausgelöst wurde und ich nicht mit -O2 kompiliert habe. Für 50000! Auf einem Dual-Core-MacBook bekomme ich:
Gesamtzeiten für einige verschiedene Werte, die erste Spalte ist die Golfparallele, die zweite ist die naive sequentielle Version:
Als Referenz ist die naive sequentielle Version diese (die mit -O2 kompiliert wurde):
quelle
Ruby - 111 + 56 = 167 Zeichen
Dies ist ein Skript mit zwei Dateien, die Hauptdatei (
fact.rb
):die extra Datei (
f2.rb
):Nimmt einfach die Anzahl der Prozesse und die zu berechnende Anzahl als Argumente und teilt die Arbeit in Bereiche auf, die jeder Prozess einzeln berechnen kann. Dann multipliziert man die Ergebnisse am Ende.
Dies zeigt wirklich, wie viel langsamer Rubinius zu YARV ist:
Rubinius:
Ruby1.9.2:
(Beachten Sie das Extra
0
)quelle
inject(:+)
. Hier ist das Beispiel aus der Dokumentation:(5..10).reduce(:+)
.8
wo es einen hätte geben sollen,*
wenn jemand Probleme damit hatte.Java,
523 519 434 430429 ZeichenDie beiden 4en in der letzten Zeile geben die Anzahl der zu verwendenden Threads an.
50000! Getestet mit dem folgenden Framework (ungolfed version der Originalversion und mit ein paar weniger Bad Practices - obwohl es immer noch reichlich ist) ergibt das (auf meinem 4-Core Linux Rechner) mal
Beachten Sie, dass ich den Test aus Fairnessgründen mit einem Thread wiederholt habe, da sich das JIT möglicherweise erwärmt hat.
Java mit Bigint ist nicht die richtige Sprache zum Golfen (schau dir an, was ich tun muss, um die elenden Dinge zu konstruieren, denn der Konstruktor, der lange dauert, ist privat), aber hey.
Aus dem ungolfed Code sollte völlig ersichtlich sein, wie sich die Arbeit aufteilt: Jeder Thread multipliziert eine Äquivalenzklasse mit der Anzahl der Threads. Der entscheidende Punkt ist, dass jeder Thread ungefähr den gleichen Arbeitsaufwand leistet.
quelle
CSharp -
206215 ZeichenTeilt die Berechnung mit der Funktionalität C # Parallel.For () auf.
Bearbeiten; Passwortsperre
Ausführungszeiten:
quelle
Perl, 140
Übernimmt
N
von der Standardeingabe.Eigenschaften:
Benchmark:
quelle
Scala (
345266244232214 Zeichen)Schauspieler verwenden:
Bearbeiten - Verweise auf entfernt
System.currentTimeMillis()
, ausgeklammerta(1).toInt
, geändert vonList.range
nachx to y
Edit 2 - Änderte die
while
Schleife in afor
, änderte die linke Falte in eineBigInt
Listenfunktion, die dasselbe tut und sich auf implizite Typkonvertierungen stützt, sodass der 6- Zeichen-Typ nur einmal erscheint, und änderte println zu printEdit 3 - Erfahren Sie, wie Sie mehrere Deklarationen in Scala erstellen können
Edit 4 - verschiedene Optimierungen, die ich gelernt habe, seit ich das erste Mal gemacht habe
Ungolfed-Version:
quelle
Scala-2.9.0 170
ungolfed:
Die Fakultät von 10 wird für 4 Kerne berechnet, indem 4 Listen generiert werden:
die parallel multipliziert werden. Es hätte einen einfacheren Ansatz für die Verteilung der Zahlen gegeben:
Aber die Verteilung wäre nicht so gut - die kleineren Zahlen würden alle in derselben Liste enden, die höchsten in einer anderen, was zu einer längeren Berechnung in der letzten Liste führen würde (für hohe Ns wäre der letzte Thread nicht fast leer , enthalten aber mindestens (N / Kerne) -Kernelemente.
Scala in Version 2.9 enthält parallele Sammlungen, die den parallelen Aufruf selbst handhaben.
quelle
Erlang - 295 Zeichen.
Das erste, was ich jemals in Erlang geschrieben habe, also wäre ich nicht überrascht, wenn jemand dies leicht halbieren könnte:
Verwendet dasselbe Threading-Modell wie mein früherer Ruby-Eintrag: Teilt den Bereich in Unterbereiche auf und multipliziert die Bereiche in separaten Threads und multipliziert dann die Ergebnisse zurück in den Haupt-Thread.
Ich konnte nicht herausfinden, wie ich escript zum Laufen bringen kann, also speichere
f.erl
und öffne erl und starte:mit entsprechenden Substitutionen.
Auf meinem MacBook Air (Dual-Core) wurden für 50000 in 2 Prozessen ungefähr 8 Sekunden und für 1 Prozess 10 Sekunden erreicht.
Hinweis: Nur bemerkt, dass es einfriert, wenn Sie versuchen, mehr Prozesse als die Zahl zu faktorisieren.
quelle