Wie viel Aufwand kann das-fPIC-flag hinzufügen?
Frage
Teste ich einen einfachen code, der berechnet Mandelbrot-Fraktale. Ich habe die überprüfung seiner Leistung abhängig von der Anzahl der Iterationen in der Funktion, die prüft, ob ein Punkt gehört zur Mandelbrot-Menge oder nicht.
Die überraschende Sache ist, dass ich immer einen großen Unterschied in den Zeiten nach dem hinzufügen der -fPIC
Flagge. Von was ich gelesen, der overhead ist in der Regel vernachlässigbar und die höchste überkopf-ich gestoßen bin, war etwa 6%. Ich gemessen um 30% overhead. Jede Beratung wird geschätzt!
Details zu meinem Projekt
Ich die -O3
Flagge, gcc 4.7.2, Ubuntu 12.04.2, x86_64.
Die Ergebnisse sehen wie folgt
#iter C (fPIC) C C/C(fPIC) 1 0.01 0.01 1.00 100 0.04 0.03 0.75 200 0.06 0.04 0.67 500 0.15 0.1 0.67 1000 0.28 0.19 0.68 2000 0.56 0.37 0.66 4000 1.11 0.72 0.65 8000 2.21 1.47 0.67 16000 2.88 4.42 0.65 32000 8.8 5.77 0.66 64000 17.6 11.53 0.66
Befehle, die ich verwenden:
gcc -O3 -fPIC fractalMain.c fractal.c -o ffpic
gcc -O3 fractalMain.c fractal.c -o f
Code: fractalMain.c
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
#include "fractal.h"
int main()
{
int iterNumber[] = {1, 100, 200, 500, 1000, 2000, 4000, 8000, 16000, 32000, 64000};
int it;
for(it = 0; it < 11; ++it)
{
clock_t start = clock();
fractal(iterNumber[it]);
clock_t end = clock();
double millis = (end - start)*1000 / CLOCKS_PER_SEC/(double)1000;
printf("Iter: %d, time: %lf \n", iterNumber[it], millis);
}
return 0;
}
Code: Fraktale.h
#ifndef FRACTAL_H
#define FRACTAL_H
void fractal(int iter);
#endif
Code: Fraktale.c
#include <stdio.h>
#include <stdbool.h>
#include "fractal.h"
void multiplyComplex(double a_re, double a_im, double b_re, double b_im, double* res_re, double* res_im)
{
*res_re = a_re*b_re - a_im*b_im;
*res_im = a_re*b_im + a_im*b_re;
}
void sqComplex(double a_re, double a_im, double* res_re, double* res_im)
{
multiplyComplex(a_re, a_im, a_re, a_im, res_re, res_im);
}
bool isInSet(double P_re, double P_im, double C_re, double C_im, int iter)
{
double zPrev_re = P_re;
double zPrev_im = P_im;
double zNext_re = 0;
double zNext_im = 0;
double* p_zNext_re = &zNext_re;
double* p_zNext_im = &zNext_im;
int i;
for(i = 1; i <= iter; ++i)
{
sqComplex(zPrev_re, zPrev_im, p_zNext_re, p_zNext_im);
zNext_re = zNext_re + C_re;
zNext_im = zNext_im + C_im;
if(zNext_re*zNext_re+zNext_im*zNext_im > 4)
{
return false;
}
zPrev_re = zNext_re;
zPrev_im = zNext_im;
}
return true;
}
bool isMandelbrot(double P_re, double P_im, int iter)
{
return isInSet(0, 0, P_re, P_im, iter);
}
void fractal(int iter)
{
int noIterations = iter;
double xMin = -1.8;
double xMax = 1.6;
double yMin = -1.3;
double yMax = 0.8;
int xDim = 512;
int yDim = 384;
double P_re, P_im;
int nop;
int x, y;
for(x = 0; x < xDim; ++x)
for(y = 0; y < yDim; ++y)
{
P_re = (double)x*(xMax-xMin)/(double)xDim+xMin;
P_im = (double)y*(yMax-yMin)/(double)yDim+yMin;
if(isMandelbrot(P_re, P_im, noIterations))
nop = x+y;
}
printf("%d", nop);
}
Geschichte hinter dem Vergleich
Er sieht zwar etwas künstlich hinzuzufügen -fPIC
flag beim erstellen ausführbare Datei (wie in einem der Kommentare). So, ein paar Worte der Erklärung: zuerst habe ich nur die kompilierte Programm als ausführbare Datei und wollte vergleichen, um meine Lua-code, die fordert, die isMandelbrot Funktion von C. So habe ich eine shared-object zu nennen, die aus lua - und hatte großen Unterschiede. Aber Sie konnte nicht verstehen, warum Sie wuchsen mit der Anzahl der Iterationen. Am Ende fand heraus, dass es war, weil der -fPIC
. Wenn ich ein kleines c-Programm, das ruft mein lua-Skript (also effektiv ich mache das gleiche, nur nicht brauchen, die .so) - die Zeiten sind sehr ähnlich zu C (ohne -fPIC
). Also ich habe es in ein paar Konfigurationen in den letzten paar Tagen und es konsequent zeigt zwei Sätze von sehr ähnlichen Ergebnissen: schneller ohne -fPIC
und langsamer mit ihm.
- Nicht reproduzieren kann mit gcc 4.7.2 auf x86_64 (OS/X).
- Du hast vergessen zu geben, die
fractal.h
header - Haben Sie etwas in
CFLAGS
? - also sind Sie immer ähnlich, mal unabhängig von der Flagge?
- Ja, fast identischen timings (z.B. 10.985 vs 10.976).
- Ich bin ein bisschen überrascht über Ihre Ergebnisse. Du bist nicht wirklich testen
-fPIC
overhead Weg. Da baut man eine ausführbare Datei, gcc kann machen einige Annahmen über den Ort der endgültigen Aufruf der Funktion. Sie brauchen, um zu bauen ein Teil Ihrer Anwendung als shared library, und der Aufwand wird in den anrufen, die Sie brauchen, um aus der primären Anwendung in der gemeinsam genutzten Bibliothek. Obwohl, in der Sie aktuelle aufgeteilt, auch dies wäre ein deutlicher Unterschied. Man müsste entwedermultiplyComplex
odersqComplex
in die gemeinsam genutzte Bibliothek, um wirklich zu sehen, beginnen einen Einfluss. - FWIW, ich auch bekommen ähnliche timings zwischen den Pisten, wie @OlaM.
- Starynkevitch: Hinzugefügt den Kopf auf die Frage.
- Nein, checked 'echo $CFLAGS' - es ist leer.
- Ich bekomme die Meldung: gcc 12.78/18.23 -- clang 13.73 / 13.75
- Interessant. Ich sehe den Unterschied auch unter Linux. Alles in
fractal.c
statische außerfractal()
die gibt die timings die gleichen zwischen-Implementierungen. Ich bin mir nicht sicher, warumgcc
ist nicht die Optimierung dieser besser unter Linux. - Ich fügte hinzu, die Erklärung, wo das problem begann, auf die Frage - haben Sie überprüft auch den Aufbau einer ordnungsgemäßen .so.
- Werfen Sie einen Blick auf die
objdump -d
Ausgabe für jede ausführbare Datei. Sie können sehen, dass einige Optimierungen sind ausgeschlossen in der-fPIC
version in denfractal()
Funktion. Was genau wollen Sie zur Messung der Leistung von hier? Der overhead für den Aufruffractal()
wenn es kompiliert mit-fPIC
? Oder machst du den fraktalen funktioniert in Lua und ruft auf der unteren Ebene Routinen? Ein Grund, warum die nicht-fPIC-version so viel besser ist, weil eine Menge Arbeit ist eingebettet infractal()
. In der-fPIC
version, ist es nicht. Wieder, können Sie beheben, indem die Helfer statisch. - vielen Dank für den Kommentar. Was ich Suche ist fair performance-Vergleich zwischen der reinen C und mein lua+C-Programme. Und Frage mich, ob der overhead nicht mit meiner unvollkommenen Umsetzung - die it sieht aus wie es ist: du hast Recht, die Hilfsfunktionen, die sollte statisch sein, und es wird verhindert, dass der overhead. Auch aus den anderen Kommentaren sieht es aus wie clang ist besser bei der Optimierung unvollkommen code selbst.
- Blöde Frage, aber Sie sind ganz sicher, dass Sie die Kompilierung als 64-bit-code, richtig? Die IA-32-Befehlssatz nicht für die position-independent code, und es wäre normal, zu sehen, diese Art von Unterschied, wenn Sie versehentlich benutzen. x86_64 war entworfen, um position-independent code so schnell wie position-dependent code, und die normale situation ist, dass kein messbarer Unterschied (wie NPE und anderen gefunden)
- Cuoq: ja, Target: x86_64-linux-gnu. Und ja, ich lese auch, dass der Unterschied sollte vernachlässigbar sein, insbesondere für 64-bit...
- Man kann auch
-flto
sowohl zur compile-und link-Zeit (neben der-O3
), und Sie könnten auch geben-mtune=native
- Mit gcc-4.8 und GLibc 2.17 auf einem Linux-kernel 3.8.5 (x86-64 i3770K, Debian/Sid/AMD64 mit ein bisschen experimentell) ich bin immer 27.55 s für PIC und 18.24 s für nonPIC; ich habe keine Ahnung, warum so ein großer Unterschied...
- Allerdings, wenn Sie kompilieren mit
gcc-4.8 -flto -O3 -mtune=native
mit oder ohne-fPIC
ich bin immer nur 18.28 sec für nicht-PIC und 18.36 für PIC. Also ich empfehle-flto -mtune=native -O3
mit oder ohne-fPIC
- OS X setzt position-unabhängigen code in den ausführbaren Dateien, nicht nur Bibliotheken. Also gcc ist gezwungen, Sie zu aktivieren
-fPIE
für die ausführbaren Dateien, nicht nur Bibliotheken. (Auf x86-64, ich glaube nicht, dass es viel wenn ein Unterschied zwischen-fPIC
und-fPIE
, aber-fPIE
könnte in der Lage sein, um die Vorteile zu nehmen von Dingen, die code-Bibliothek konnte nicht). Linux und Windows haben nicht diese Anforderung, so macht es Sinn, dass wenn es einen Unterschied gibt, ist es nicht reproduzierbar auf OS X.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es stellt sich heraus, dass, wenn Sie kompilieren, ohne die
-fPIC
optionmultiplyComplex
,sqComplex
,isInSet
undisMandelbrot
sind inline automatisch durch den compiler. Wenn Sie definieren diese Funktionen als statische, werden Sie wahrscheinlich erhalten die gleiche Leistung beim kompilieren mit-fPIC
da der compiler frei inlining durchführen.Der Grund, warum der compiler nicht automatisch inline-helper-Funktionen zu tun hat, mit symbol interposition. Position independent code ist erforderlich, um Zugriff auf alle globalen Daten, die indirekt, d.h. durch die globalen offset-Tabelle. Die gleiche Einschränkung gilt für Funktionsaufrufe, die durch zu gehen das Verfahren linkage table. Da ein symbol bekommen könnte zwischengeschaltet, durch eine zur Laufzeit (siehe
LD_PRELOAD
), der compiler kann nicht einfach davon ausgehen, dass es sicher ist, um inline-Funktion mit globaler Sichtbarkeit.Die gleiche Annahme kann gemacht werden, wenn Sie kompilieren, ohne
-fPIC
, d.h. der compiler kann sicher davon ausgehen, dass eine global definierte symbol in der ausführbaren Datei kann nicht zwischengeschaltet werden, da der lookup-Bereich beginnt mit der ausführbaren Datei selbst, das ist dann gefolgt von allen anderen Bibliotheken, darunter die vorinstallierte ersetzt.Für ein besseres Verständnis einen Blick auf den folgenden Papier.
Als andere Leute bereits darauf hingewiesen
-fPIC
Kräfte GCC zu deaktivieren viele Optimierungen wie z.B. inlining und das Klonen. Ich möchte an dieser Stelle mehrere Möglichkeiten, dies zu überwinden:-fPIC
mit-fPIE
wenn Sie kompilieren Hauptprogramm (nicht für Bibliotheken); dies ist Standard auf modernen Distributionen aus Sicherheitsgründen-fvisibility=hidden
und__attribute__((visibility("default")))
exportieren nur die benötigten Funktionen aus der Bibliothek aus und verstecken den rest; dies würde es erlauben, den GCC zu optimieren, versteckte Funktionen__attribute__((alias ("__f")));
) zu beziehen, um library-Funktionen aus der Bibliothek; dies würde wieder lösen GCC Hände-fno-semantic-interposition
Flagge Hinzugefügt wurde, in den letzten GCC-VersionenEs ist interessant zu beachten Sie, dass das Geräusch unterscheidet sich von GCC, wie es erlaubt alle Optimierungen standardmäßig unabhängig von
-fPIC
(kann überschrieben werden, mit-fsemantic-interposition
zu erhalten GCC-wie Verhalten).Wie andere schon besprochen in den Kommentaren Ihrer post öffnen, kompilieren mit
-flto
sollte dazu beitragen, den Unterschied in den Laufzeiten, die Sie sehen, für diesen speziellen Fall, da der link mal Optimierungen von gcc wird wahrscheinlich herausfinden, dass es tatsächlich ok zu inline ein paar Funktionen 😉Im Allgemeinen, zur link-Zeit Optimierungen konnte führen zu massiven Kürzungen in der code-Größe (~6%) link zum paper auf den link Zeit, die Optimierungen in gold, und damit die Laufzeit sowie (mehr von Ihr Programm passt in den cache). Beachten Sie auch, dass
-fPIC
ist meist angezeigt, als ein feature, das ermöglicht eine höhere Sicherheit und ist immer aktiviert in android. Diese Frage, SO kurz erläutert, wie gut. Auch, nur, damit Sie wissen,-fpic
ist die schnellere version von-fPIC
, so dass, wenn Sie verwenden müssen-fPIC
versuchen-fpic
statt - link zum gcc-docs. Für x86-könnte es nicht einen Unterschied machen, aber Sie brauchen, um dies selbst zu überprüfen/Frage auf gcc-help.-fPIC
hört nicht auf inlining, oder es kann nicht geschehen, in jedem Fall (weil OSX unterstützt symbol interposition). TL;DR:static
gut ist!call
Anweisung verwendet einen 32-bit relativ-Verschiebung.)zlib
(gzip) ist eines der besten Beispiele für high-performance-software, die kompiliert wird, wie eine Bibliothek: rechenintensiv, aber nicht mit asm. Es gibt auch OpenSSL -, BLAS / LAPACK-Bibliotheken (wie ATLAS), video-Encoder wie libx264, OpenGL, Treiber, GUI-Bibliotheken,libc
,libm
,libstdc++
usw. Nicht zu erwähnen, dass eine Menge von desktop-software baut die meisten seiner code-Bibliotheken. z.B. Chrom, baut der v8-javascript-engine-Bibliothek:/usr/lib/chromium-browser/libs/libv8.so
.ldd
auf viele GUI-Programme zeigen riesige Mengen von Bibliotheken, einige von Ihnen mit wichtigen code für das Programm.-fPIC
(z.B. function-inlining oder Klonen ist grundsätzlich deaktiviert). Dies geschieht durch die design-support-symbol erfolgen. Möglichkeiten, dies zu überwinden sind, um zu verwenden, private symbol-Aliase-fno-semantic-interposition
und hidden die Sichtbarkeit. Lesen Sie dieser berüchtigten post für weitere details.-flto
noch hat, zu bewahren, die interposition Semantik also ich glaube nicht, dass Sie werde in der Lage sein zu tun, einer nicht-fPIC-freundlich-Optimierungen.static
auf kleine Funktionen in der gleichen übersetzungseinheit. Auch -fPIE hat einige Kosten (auf x86), aber es hat nicht symbol interposition. Ich schrieb darüber, dass vs. PIC in 32-bit absolute Adressen dürfen nicht mehr in x86-64 Linux?