Wissenschaftliche Software unterscheidet sich nicht wesentlich von anderer Software, da sie weiß, was optimiert werden muss.
Die Methode, die ich benutze, ist zufälliges Anhalten . Hier sind einige der Speedups, die es für mich gefunden hat:
Wenn ein großer Teil der Zeit in Funktionen wie log
und verbracht wird exp
, kann ich die Argumente für diese Funktionen als Funktion der Punkte sehen, von denen aus sie aufgerufen werden. Oft werden sie wiederholt mit demselben Argument aufgerufen. Wenn dies der Fall ist, führt das Speichern von Memos zu einer massiven Beschleunigung.
Wenn ich BLAS- oder LAPACK-Funktionen verwende, verbringe ich möglicherweise einen großen Teil der Zeit mit Routinen zum Kopieren von Arrays, Multiplizieren von Matrizen, Choleski-Transformationen usw.
Die Routine zum Kopieren von Arrays dient nicht der Geschwindigkeit, sondern der Benutzerfreundlichkeit. Möglicherweise gibt es eine weniger bequeme, aber schnellere Möglichkeit, dies zu tun.
Routinen zum Multiplizieren oder Invertieren von Matrizen oder zum Ausführen von Choleski-Transformationen enthalten in der Regel Zeichenargumente, mit denen Optionen wie 'U' oder 'L' für das obere oder untere Dreieck angegeben werden. Auch diese sind der Einfachheit halber da. Da meine Matrizen nicht sehr groß waren, verbrachten die Routinen mehr als die Hälfte ihrer Zeit damit, die Unterroutine zum Vergleichen von Zeichen aufzurufen , um die Optionen zu entschlüsseln. Das Schreiben von Spezialversionen der teuersten mathematischen Routinen führte zu einer enormen Beschleunigung.
Wenn ich nur auf Letzteres eingehen kann: Die Matrix-Multiplikationsroutine DGEMM ruft LSAME auf, um ihre Zeichenargumente zu dekodieren. Betrachtet man die prozentuale Inklusivzeit (die einzige Statistik, die es wert ist, betrachtet zu werden), können Profiler, die als "gut" eingestuft werden, DGEMM mit einem Anteil von 80% an der Gesamtzeit und LSAME mit einem Anteil von 50% an der Gesamtzeit anzeigen. Wenn Sie das erstere betrachten, werden Sie versucht sein zu sagen: "Nun, es muss stark optimiert werden, also kann ich nicht viel dagegen tun." Wenn Sie sich das letztere ansehen, werden Sie versucht sein zu sagen: "Huh? Worum geht es hier? Das ist nur eine winzige Routine. Dieser Profiler muss falsch sein!"
Es ist nicht falsch, es sagt dir nur nicht, was du wissen musst. Aus zufälligen Pausen geht hervor, dass sich DGEMM auf 80% der Stack-Samples und LSAME auf 50% befindet. (Sie benötigen nicht viele Beispiele, um dies zu erkennen. In der Regel reichen 10 aus.) Außerdem ruft DGEMM bei vielen dieser Beispiele LSAME aus verschiedenen Codezeilen auf.
Jetzt wissen Sie also, warum beide Routinen so viel Zeit in Anspruch nehmen. Sie wissen auch, von wo in Ihrem Code sie aufgerufen werden, um die ganze Zeit zu verbringen. Das ist der Grund, warum ich willkürlich pausiere und die Profiler aus einer krassen Perspektive betrachte, egal wie gut sie sind. Sie sind mehr daran interessiert, Messungen zu erhalten, als Ihnen zu sagen, was los ist.
Es ist leicht anzunehmen, dass die mathematischen Bibliotheksroutinen bis zum n-ten Grad optimiert wurden, aber tatsächlich wurden sie so optimiert, dass sie für eine Vielzahl von Zwecken verwendet werden können. Sie müssen sehen, was wirklich los ist, und nicht, was leicht anzunehmen ist.
ADDED: Um Ihre letzten beiden Fragen zu beantworten:
Was sind die wichtigsten Dinge, die Sie zuerst ausprobieren sollten?
Nehmen Sie 10-20 Stapelproben und fassen Sie sie nicht nur zusammen, sondern verstehen Sie, was jede einzelne Ihnen sagt. Tun Sie dies zuerst, zuletzt und dazwischen. (Es gibt keinen "Versuch", junger Skywalker.)
Woher weiß ich, wie viel Leistung ich erreichen kann?
Anhand der Stapelproben können Sie sehr grob abschätzen, welcher Bruchteil der Zeit eingespart wird. (Es folgt eine -Verteilung, wobei die Anzahl der Samples ist, die angezeigt haben, was Sie korrigieren möchten, und die Gesamtzahl der Samples ist Kosten für den Code, den Sie zum Ersetzen verwendet haben, der hoffentlich klein sein wird.) Dann beträgt das Beschleunigungsverhältnis was groß sein kann. Beachten Sie, wie sich dies mathematisch verhält. Wenn und , ist der Mittelwert und die Mode von 0,5 für ein Beschleunigungsverhältnis von 2. Hier ist die Verteilung:
Wenn Sie risikoavers sind, gibt es eine kleine Wahrscheinlichkeit (0,03%). Dasxβ(s+1,(n−s)+1)sn1/(1−x)n=10s=5x
x ist kleiner als 0,1 für eine Beschleunigung von weniger als 11%. Dies ist jedoch die gleiche Wahrscheinlichkeit, dass größer als 0,9 ist, für ein Beschleunigungsverhältnis von mehr als 10! Wenn Sie Geld im Verhältnis zur Programmgeschwindigkeit erhalten, sind das keine schlechten Chancen.x
Wie ich Ihnen bereits gesagt habe, können Sie den gesamten Vorgang wiederholen, bis Sie nicht mehr können, und das zusammengesetzte Beschleunigungsverhältnis kann recht groß sein.
HINZUGEFÜGT: Lassen Sie mich als Reaktion auf Pedros Besorgnis über falsche Positive versuchen, ein Beispiel zu konstruieren, an dem deren Auftreten zu erwarten ist. Wir werden niemals auf ein potenzielles Problem einwirken, es sei denn, wir sehen es zweimal oder öfter. Daher würden wir erwarten, dass falsche Positive auftreten, wenn wir ein Problem so selten wie möglich sehen, insbesondere wenn die Gesamtzahl der Stichproben groß ist. Angenommen, wir nehmen 20 Proben und sehen sie zweimal. Das schätzt seine Kosten auf 10% der gesamten Ausführungszeit, der Art seiner Verteilung. (Der Mittelwert der Verteilung ist höher - es ist .) Die untere Kurve in der folgenden Grafik ist die Verteilung:(s+1)/(n+2)=3/22=13.6%
Überlegen Sie, ob wir bis zu 40 Proben genommen haben (mehr als jemals zuvor) und nur bei zwei von ihnen ein Problem festgestellt haben. Die geschätzten Kosten (Modus) dieses Problems betragen 5%, wie in der größeren Kurve gezeigt.
Was ist ein "falsch positives"? Wenn Sie ein Problem beheben, bemerken Sie einen so geringen Gewinn als erwartet, dass Sie es bereuen, ihn behoben zu haben. Die Kurven zeigen (wenn das Problem "klein" ist), dass die Verstärkung zwar geringer sein kann als der Bruchteil der Proben, die sie zeigen, aber im Durchschnitt größer ist.
Es besteht ein weitaus größeres Risiko - ein "falsches Negativ". In diesem Fall liegt ein Problem vor, das jedoch nicht gefunden wird. (Dazu trägt eine "Bestätigungsverzerrung" bei, bei der fehlende Beweise eher als Beweise für die Abwesenheit behandelt werden.)
Was Sie mit einem Profiler erhalten (ein gutes) ist man viel genauere Messung zu erhalten (also weniger Chancen von Fehlalarmen), auf Kosten von viel weniger präzise Informationen über das, was das Problem tatsächlich ist (also weniger Chancen, es zu finden und immer irgendein Gewinn). Dies begrenzt die insgesamt erreichbare Beschleunigung.
Ich möchte Benutzer von Profilern ermutigen, die tatsächlich in der Praxis auftretenden Beschleunigungsfaktoren anzugeben.
Es gibt noch einen weiteren Punkt, der noch zu klären ist. Pedros Frage zu False Positives.
Er erwähnte, dass es schwierig sein könnte, kleine Probleme in hochoptimiertem Code zu lösen. (Für mich ist ein kleines Problem eines, das 5% oder weniger der Gesamtzeit ausmacht.)
Da es durchaus möglich ist, ein Programm zu erstellen, das mit Ausnahme von 5% absolut optimal ist, kann dieser Punkt wie in dieser Antwort nur empirisch behandelt werden . Um aus empirischen Erfahrungen zu verallgemeinern:
Ein Programm enthält in der Regel mehrere Optimierungsmöglichkeiten. (Wir können sie als "Probleme" bezeichnen, aber sie sind oftmals perfekter Code, der sich einfach erheblich verbessern lässt.) Dieses Diagramm zeigt ein künstliches Programm, das einige Zeit in Anspruch nimmt (z. B. 100s), und es enthält die Probleme A, B, C, ... das, wenn es gefunden und repariert wird, 30%, 21% usw. der ursprünglichen 100er einspart.
Beachten Sie, dass Problem F 5% der ursprünglichen Zeit kostet, es ist also "klein" und ohne 40 oder mehr Proben schwer zu finden.
Die ersten 10 Beispiele finden jedoch leicht das Problem A. ** Wenn dies behoben ist, dauert das Programm nur 70 Sekunden, bei einer Geschwindigkeitssteigerung von 100/70 = 1,43x. Das macht das Programm nicht nur schneller, es vergrößert auch die Prozentsätze der verbleibenden Probleme um dieses Verhältnis. Zum Beispiel hat Problem B ursprünglich 21 Sekunden in Anspruch genommen, was 21% der Gesamtsumme ausmachte, aber nach dem Entfernen von A benötigt B 21 Sekunden von 70 Sekunden oder 30%, so dass es einfacher ist, zu finden, wann der gesamte Prozess wiederholt wird.
Wenn der Prozess fünf Mal wiederholt wird, beträgt die Ausführungszeit 16,8 Sekunden. Davon ist das Problem F 30% und nicht 5%, sodass 10 Stichproben es leicht finden.
Das ist also der Punkt. Empirisch gesehen enthalten Programme eine Reihe von Problemen mit einer Größenverteilung, und jedes festgestellte und behobene Problem erleichtert das Auffinden der verbleibenden Probleme. Um dies zu erreichen, kann keines der Probleme übersprungen werden, da sie, falls vorhanden, Zeit in Anspruch nehmen, die Gesamtbeschleunigung begrenzen und die verbleibenden Probleme nicht vergrößern.
Deshalb ist es sehr wichtig, die versteckten Probleme zu finden .
Wenn die Probleme A bis F gefunden und behoben werden, beträgt die Beschleunigung 100 / 11,8 = 8,5x. Wenn einer von ihnen verfehlt wird, zum Beispiel D, dann beträgt die Beschleunigung nur 100 / (11,8 + 10,3) = 4,5x.
Das ist der Preis für falsche Negative.
Wenn der Profiler also sagt, dass es hier kein nennenswertes Problem zu geben scheint (dh ein guter Codierer, dies ist praktisch optimaler Code), ist es vielleicht richtig und vielleicht auch nicht. (Ein falsches Negativ .) Sie wissen nicht genau, ob weitere Probleme zu beheben sind, um eine höhere Geschwindigkeit zu erzielen, es sei denn, Sie versuchen eine andere Profilerstellungsmethode und stellen fest, dass dies der Fall ist. Meiner Erfahrung nach erfordert die Profilierungsmethode keine große Anzahl von Stichproben, sondern eine kleine Anzahl von Stichproben, wobei jede Stichprobe gründlich genug verstanden wird, um Optimierungsmöglichkeiten zu erkennen.
** Es dauert mindestens 2 Treffer, um ein Problem zu finden, es sei denn, man weiß vorher, dass es eine (nahezu) Endlosschleife gibt. (Die roten Häkchen stehen für 10 Zufallsstichproben); Die durchschnittliche Anzahl von Stichproben, die benötigt wird, um 2 oder mehr Treffer zu erhalten, beträgt bei einem Problem von 30% ( negative Binomialverteilung ). 10 Proben finden es mit einer Wahrscheinlichkeit von 85%, 20 Proben - 99,2% ( Binomialverteilung ). Um die Wahrscheinlichkeit, das Problem zu bekommen, in R, zu bewerten , zum Beispiel: .2/0.3=6.671 - pbinom(1, numberOfSamples, sizeOfProblem)
1 - pbinom(1, 20, 0.3) = 0.9923627
HINZUGEFÜGT: Der eingesparte Zeitanteil folgt einer Beta-Verteilung , wobei die Anzahl der Stichproben ist und die Anzahl, die das Problem anzeigt. Das Beschleunigungsverhältnis gleich (vorausgesetzt, dass alles von gespeichert ist), und es wäre interessant, die Verteilung von zu verstehen . Es stellt sich heraus, dass einer BetaPrime- Distribution folgt . Ich habe es mit 2 Millionen Samples simuliert und dabei folgendes Verhalten festgestellt:β ( s + 1 , ( n - s ) + 1 ) n s y 1 / ( 1 - x ) x y y - 1xβ(s+1,(n−s)+1)nsy1/(1−x)xyy−1
distribution of speedup
ratio y
s, n 5%-ile 95%-ile mean
2, 2 1.58 59.30 32.36
2, 3 1.33 10.25 4.00
2, 4 1.23 5.28 2.50
2, 5 1.18 3.69 2.00
2,10 1.09 1.89 1.37
2,20 1.04 1.37 1.17
2,40 1.02 1.17 1.08
3, 3 1.90 78.34 42.94
3, 4 1.52 13.10 5.00
3, 5 1.37 6.53 3.00
3,10 1.16 2.29 1.57
3,20 1.07 1.49 1.24
3,40 1.04 1.22 1.11
4, 4 2.22 98.02 52.36
4, 5 1.72 15.95 6.00
4,10 1.25 2.86 1.83
4,20 1.11 1.62 1.31
4,40 1.05 1.26 1.14
5, 5 2.54 117.27 64.29
5,10 1.37 3.69 2.20
5,20 1.15 1.78 1.40
5,40 1.07 1.31 1.17
Die ersten beiden Spalten geben das 90% -Konfidenzintervall für das Beschleunigungsverhältnis an. Das mittlere Beschleunigungsverhältnis ist gleich Ausnahme des Falls, in dem . In diesem Fall ist es undefiniert, und wenn ich die Anzahl der simulierten Werte erhöhe, steigt der empirische Mittelwert.s = n y(n+1)/(n−s)s=ny
Dies ist eine grafische Darstellung der Verteilung der Beschleunigungsfaktoren und ihrer Mittelwerte für 2 Treffer aus 5, 4, 3 und 2 Stichproben. Wenn zum Beispiel 3 Proben entnommen werden und 2 davon auf ein Problem stoßen und dieses Problem behoben werden kann, beträgt der durchschnittliche Beschleunigungsfaktor 4x. Wenn die 2 Treffer nur in 2 Samples zu sehen sind, ist die durchschnittliche Beschleunigung undefiniert - konzeptionell, weil Programme mit Endlosschleifen mit einer Wahrscheinlichkeit ungleich Null existieren!