Hallo zusammen, ich habe folgendes Problem: Ich habe mehrere Assembler-Funktionen die ich bereits zu einem COFF-Obj-File kompiliert habe. Nun würde ich gerne diese Funktionen in einem C/C++ Projekt benutzen (Visual Studio 2008 TS). An und für sich kein Problem. Ich habe die Funktionen dort als extern "C" deklariert und kann sie dort auch verwenden. Da diese Funktionen jedoch weit über 2 Millionen mal am Stück aufgerufen werden und es sich hier um einen zeitkritischen Prozess handelt, möchte ich die ständigen calls vermeiden und die Funktion lieber inlinen. Da erst dem Linker die Funktion bekannt gemacht wird, hat der Compiler keine Chance dazu. Was kann ich tun? Eine Möglichkeit wäre sicherlich inline assembler zu verwenden. Dies mache ich mit dem VC jedoch sehr ungern, da ich viele Redundanzen herausnehmen müsste (doppeltes Sichern von Registern). Zudem stellt sich mir die Frage ob der Compiler an meinem Code rummurkst?! Uber vorweihnachtliche Hilfe wäre ich sehr dankbar ;-)
dir Frage ist ob es sinn macht, wenn du eine Funktion schon in Assembler schreibst dann vermutlich aus dem Grund damit sie sehr schnell ist. Ich vermute es ist auch nicht bloss ein 3 Zeiler. Die Frage ist also ob wirklich der eine Call etwas an der gesamtlaufzeit ändert. Klar sind es dann millionen calls weniger aber ob es nun 10 Stunden oder 10Stunden und 5minuten dauert ist doch egal.
Meine Messungen (mit anderen Bedinungen) haben ergeben, dass es in etwa 10 Sekunden ausmacht. Dass ist in meinem Fall zu viel. Ich habe für meine Funktion eine Zeitspanne von etwa. 5 bis max. 10 Sekunden Zeit. Die erreiche ich aber nicht. Kann mir jemand sagen, ob es denn prinzipiell überhaupt möglich ist den Code aus einer externen Objektdatei zu inlinen?
Sascha H. schrieb: > ob es denn prinzipiell überhaupt möglich ist den > Code aus einer externen Objektdatei zu inlinen? Nein.
A. K. schrieb: > Nein. Angeblich schon: http://msdn.microsoft.com/en-us/library/e7k32f4k%28VS.80%29.aspx Ich habe nur noch nicht ganz verstanden, wie genau dass genutzt werden muss.
aus wenn besteht denn die funktion wenn sie so schnell ist? Bist du sicher das es mit C ohne asm nicht genauso schnell geht.
Wo steht dort, dass dies ginge? Voraussetzung für Inlining ist stets, dass der Compiler den zu inlinenden Code kennt. Hier ist aber vorgegeben, dass er den Code nicht kennt. PGO sagt dem Compiler nur, wo sich Inling laufzeitmässig lohnt und wo nicht.
A. K. schrieb: > Wo steht dort, dass dies ginge? Ich lese dort allerdings auch, dass der Linker das kann. So wie ich das verstanden habe, wird aus den Profile-Läufen die Information gewonnen, ob sich inlinen für Funktionen lohnen würde und der Linker baut das dann entsprechend um. > PGO sagt dem Compiler nur, wo sich Inling laufzeitmässig lohnt und wo > nicht. Nicht ganz. Am Anfang des Artikels ist eine Übersicht über die durchzuführenden Schritte. Nach dem Schritt der Generierung der PGO Info kommt nur noch ein Linkerschritt mit dem Optimize Schalter. Allerdings werde ich aus der Beschreibung des Linker-Schalters auch nicht richtig schlau. An einer Stelle steht, dass der Linker dann den Compiler benutzt um globale Optimierungen durchzuführen. Beim konkreten OPTIMZE Fall steht allerdings nichts davon. Da liest es sich wieder so, als ob der Linker das ganz alleine machen könnte. Hmmm Ich würde allerings auch erst mal die Frage stellen, ob es sich wirklich lohnt von C auf Assembler umzusteigen oder ob da nicht auf C-Ebene noch was geht.
@Karl heinz Buchegger Exakt, so habe ich das auch verstanden. Ich bin gerade dabei zu testen, ob es funktioniert. Prinzipiell wird offensichtlich, dass durch den Testlauf gewonnene *.pgc File dazu genutzt den Code zu optimieren.
Karl heinz Buchegger schrieb:
> Ich lese dort allerdings auch, dass der Linker das kann.
Sorry, ich bin grad blind und seh's immer noch nicht.
Sascha H. schrieb: > Exakt, so habe ich das auch verstanden. Ich bin gerade dabei zu testen, > ob es funktioniert. Prinzipiell wird offensichtlich, dass durch den > Testlauf gewonnene *.pgc File dazu genutzt den Code zu optimieren. Klar. Nur ist das nicht wirklich der Linker, bzw. nicht nur der, auf den es dabei ankommt. Zu dieser Form der Optimierung gehört beispielsweise auch die Entscheidung, welcher Zweig eines if-else Statements wichtiger ist. Der andere wird dann aus dem Weg geräumt um teure ausgeführte Sprungbefehle zu vermeiden. Das wird kaum der Linker machen. Wenn doch, dann allenfalls weil der Objektcode nur Pseudocode enthält und der Linker den eigentlichen Codegenerator.
A. K. schrieb: > Karl heinz Buchegger schrieb: > >> Ich lese dort allerdings auch, dass der Linker das kann. > > Sorry, ich bin grad blind und seh's immer noch nicht. Wie gsagt: So sicher bin ich mir auch nicht. Aber hier die durchzuführenden Schritte * Compile one or more source code files with /GL. logisch. Das Object File muss attributiert werden. * Link with /LTCG:PGINSTRUMENT. auch klar. Da muss noch eine Lib dazu, die die Infos sammelt und abspeichert * Profile the application. Auch klar. Jetzt werden Profile Daten generiert * Link with /LTGC:PGOPTIMIZE. Und jetzt werden die Profile Daten eingearbeitet. Interessant: Kein Recompile notwendig. Offenbar entfernt dieser Schritt auch die Profile-Attributierung Und das wars. Allerdings ist beim ersten Schritt ein Zusatz: However, only those modules compiled with /GL will be instrumented and later available for profile-guided optimizations. Hmm. Wie das bei Assemblermodulen geht .... keine Ahnung. Aber das müsste man rauskriegen können. Auch interessant: /LTCG steht für Link Time Code Generation Auf der anderen Seite: Namen sind Schall und Rauch :-)
Karl heinz Buchegger schrieb: > * Link with /LTGC:PGOPTIMIZE. > > Und jetzt werden die Profile Daten eingearbeitet. > Interessant: Kein Recompile notwendig. Offenbar entfernt dieser > Schritt auch die Profile-Attributierung Jo, aber genau da liegt der Hase im Pfeffer. Erklär mit bitte mal, wie ohne Recompile eine "Conditional Branch Optimization" möglich sein soll. Bloss weil du nur den Linker aufrufst, heisst das ja noch lange nicht, dass dabei nur das läuft, was man traditionell unter einem Linker versteht.
Karl heinz Buchegger schrieb: > Hmm. Wie das bei Assemblermodulen geht .... keine Ahnung. Aber das > müsste man rauskriegen können. > Auch interessant: /LTCG steht für Link Time Code Generation > Auf der anderen Seite: Namen sind Schall und Rauch :-) Ja, darüber bin ich auch gestolpert. Hört sich ja so an, als könnten dann nur solche Module optimiert werden. Zur Not benutze ich MASM. Da wird evtl. eine solche Möglichkeit bestehen. A. K. schrieb: > Bloss weil du nur den Linker aufrufst, heisst das ja noch lange nicht, > dass dabei nur das läuft, was man traditionell unter einem Linker > versteht. Behauptet ja niemand, dass das NUR der Linker macht. Und um ehrlich zu sein ist mir dass auch erst mal egal, so lange es funktioniert. Ich gehe sowieso davon aus, dass der VS-Linker mehr macht als sein Name sagt.
Aber dann kommt's sehr drauf an, was aus dem Compiler im LTCG-Modus rauskommt und ob sich das mit extern zugefüttertem Assembler-Code verträgt. Meine unmassgebliche Vermutung ist, dass der Compiler dabei halbgaren Zwischencode auswirft. Vom Assembler her steht aber nur der Binärcode (plus relocations) zur Verfügung. Ob das wohl zusammengeht? Denn eigentlich müsste dieser Linktime-Codegenerator den Fall eigens vorsehen und den Binärcode analysieren und tracen, um festzustellen welche Register der verwendet und wo der wirklich aufhört. Ist ja nicht in Stein gemeisselt, dass Assembler-Code immer mit dem letzten Byte Code aufhört. Meine Prognose: Externer Binärcode bleibt externer Binärcode. Mit oder ohne LTCG.
A. K. schrieb: > Karl heinz Buchegger schrieb: > >> * Link with /LTGC:PGOPTIMIZE. >> >> Und jetzt werden die Profile Daten eingearbeitet. >> Interessant: Kein Recompile notwendig. Offenbar entfernt dieser >> Schritt auch die Profile-Attributierung > > Jo, aber genau da liegt der Hase im Pfeffer. Erklär mit bitte mal, wie > ohne Recompile eine "Conditional Branch Optimization" möglich sein soll. Ich hab keine Ahnung :-) > Bloss weil du nur den Linker aufrufst, heisst das ja noch lange nicht, > dass dabei nur das läuft, was man traditionell unter einem Linker > versteht. Ich geh mal stark davon aus, das das was MS da hat, mit einem traditionellen Linker nicht mehr viel zu tun hat. So wie MS bei vielen Dingen ihr eigenes Süppchen kocht (oder klaut)
A. K. schrieb: > Meine unmassgebliche Vermutung ist, dass der Compiler dabei halbgaren > Zwischencode auswirft. Vom Assembler her steht aber nur der Binärcode > (plus relocations) zur Verfügung. Ob das wohl zusammengeht? Mir gehts wie dir: Ich kanns mir nicht recht vorstellen. Aber wie heißt es so schön: Versuch macht kluch.
Erstes Testergebnis: Es wird versucht folgende Funktion in einem C-Projekt zu inlinen:
1 | TITLE PGOTest.asm |
2 | |
3 | .686P |
4 | .XMM |
5 | .MODEL FLAT |
6 | |
7 | |
8 | PUBLIC _MakeSth |
9 | EXTRN __imp__printf:PROC |
10 | |
11 | |
12 | .CODE |
13 | |
14 | CONST SEGMENT |
15 | FORMAT DB '%d %d', 13, 10, 0 |
16 | CONST ENDS |
17 | |
18 | _TEXT SEGMENT |
19 | _i1$ = 8 |
20 | _i2$ = 12 |
21 | _MakeSth PROC |
22 | push ebp |
23 | mov ebp, esp |
24 | |
25 | mov eax, DWORD PTR _i2$[ebp] |
26 | mov edx, DWORD PTR _i1$[ebp] |
27 | |
28 | push eax |
29 | push edx |
30 | push OFFSET FORMAT |
31 | call DWORD PTR __imp__printf |
32 | add esp, 12 |
33 | |
34 | pop ebp |
35 | ret |
36 | _MakeSth ENDP |
37 | _TEXT ENDS |
38 | |
39 | END |
Das ganze sieht in meinem Main-C-Projekt dann so aus:
1 | #include <stdio.h> |
2 | |
3 | |
4 | extern void MakeSth(int i1, int i2); |
5 | |
6 | |
7 | int main() |
8 | {
|
9 | //MakeSth(77, 999);
|
10 | |
11 | unsigned i = 0, j = (unsigned)(1 << 8); |
12 | for( ; j; j--, i++ ) { |
13 | MakeSth(i, j); |
14 | }
|
15 | |
16 | return 0; |
17 | }
|
Nach der PG-Optimierung habe ich folgendes Assembly-Listing erhalten:
1 | ; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 |
2 | |
3 | TITLE main.cpp |
4 | .686P |
5 | .XMM |
6 | include listing.inc |
7 | .model flat |
8 | |
9 | INCLUDELIB OLDNAMES |
10 | |
11 | EXTRN @__security_check_cookie@4:PROC |
12 | EXTRN _MakeSth:PROC |
13 | PUBLIC _main |
14 | ; Function compile flags: /Ogtpy |
15 | ; COMDAT _main |
16 | _TEXT SEGMENT |
17 | _main PROC ; COMDAT |
18 | ; 2056 dynamic instrs |
19 | ; entered 1 times |
20 | ; Line 9 |
21 | push esi |
22 | push edi |
23 | ; Line 12 |
24 | xor edi, edi |
25 | mov esi, 256 ; 00000100H |
26 | npad 7 |
27 | $LL3@main: |
28 | ; Line 14 |
29 | push esi |
30 | push edi |
31 | call _MakeSth |
32 | dec esi |
33 | add esp, 8 |
34 | inc edi |
35 | test esi, esi |
36 | jne SHORT $LL3@main |
37 | ; taken 255(99%), not-taken 1(0%) |
38 | pop edi |
39 | ; Line 17 |
40 | xor eax, eax |
41 | pop esi |
42 | ; Line 18 |
43 | ret 0 |
44 | _main ENDP |
45 | _TEXT ENDS |
46 | END |
Wie man sieht hat er hier nur "Optimierungen" hinsichtlich Sprunganweisungen getroffen und eine Analyse als Kommentar hinzugefügt. Der Grund für den Call von MakeSth kann natürlich auch die Tatsache sein, dass es nicht mit dem Schalter /GL kompiliert wurde, da ja durch ml.exe (MASM) kompiliert wurde.
sehe ich das richtig das du dir wegen dem call gedanken machst aber selber in der Funktion ein printf aufrufst? Printf macht doch bestimmt 99.9% der Rechenzeit aus.
Peter schrieb: > sehe ich das richtig das du dir wegen dem call gedanken machst aber > selber in der Funktion ein printf aufrufst? Printf macht doch bestimmt > 99.9% der Rechenzeit aus. Glaubst du ehrlich, dass DAS meine ach so performante Funktion ist, die ich unbedingt inlinen will? Da kann ich mir ein schmunzeln nicht verkneifen ;-). Ich hab einfach mal ein paar Zeilen dahin geklatscht, um euch was zeigen zu können bzw. das Ganze mal zu testen. Und ob der das inlined oder nicht, ist denke ich mal unabhängig davon, ob ich darin etwas aufrufe. Meinen original Code kann ich nicht posten, da 1. zu viel und 2. Firmeneigentum.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.