Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Ich habe da mal eine Frage zu statischen Libs. Mein altes Galileo Buch ist da leider nicht sehr ausführlich. Aber wenn ich das halbwegs richtig verstanden habe, ist eine Lib, die mit ar erstellt wird, nichts weiter als ein Archiv von *.o Dateien. Aber damit bekomme ich Probleme. Ich benutze einige Programmmodule in mehreren Programmen, sowohl C als auch C++ Programme. Daher dachte ich, eine Lib wäre nicht schlecht. Aber da ich bei meiner bisherigen Arbeit vereinzelt auf Namenskonflikte gestoßen bin, habe ich die internen Funktionen der C-Module als static deklariert, damit sie in anderen Dateien nicht sichtbar sind. Nach meinigem bisherigen Verständnis ist es aber so, das dann immer die komplette *.o Datei eingelinkt wird. Das ist jetzt aber nicht in jedem Fall gewollt, da nicht alle Programme alle Funktionen benötigen. Das würde bedeuten, dass ich die Funktionen in einzelne Dateien, oder zumindest in einzelne Gruppen aufteilen müsste. Das wäre zwar kein Problem, aber dann bekäme ich wohl wieder Probleme mit der static Deklaration einzelner Funktionen (da diese dann nur innerhalb der Datei gültig sind und somit Linker Fehler werfen würden). Gibt es da einen Trick, der mein Problem lösen könnte?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12801
|
Dakuan schrieb:
Nach meinigem bisherigen Verständnis ist es aber so, das dann immer die komplette *.o Datei eingelinkt wird. Das ist jetzt aber nicht in jedem Fall gewollt, da nicht alle Programme alle Funktionen benötigen.
Das ist aber die kleinste Einheit, auf der entschieden werden kann, ob sie benutzt wird oder nicht.
Das würde bedeuten, dass ich die Funktionen in einzelne Dateien, oder zumindest in einzelne Gruppen aufteilen müsste.
Genau. Aber warum ist das überhaupt so problematisch? Heutige Rechner haben doch so viel Speicher, dass man sich um ein bisschen zusätzlichen Code nicht wirklich Gedanken machen muss.
Das wäre zwar kein Problem, aber dann bekäme ich wohl wieder Probleme mit der static Deklaration einzelner Funktionen (da diese dann nur innerhalb der Datei gültig sind und somit Linker Fehler werfen würden). Gibt es da einen Trick, der mein Problem lösen könnte?
Du packst allen Zustand (also die static -Variablen), der zusammen gehört, in eine .o-Datei und packst auch die Zugriffsfunktionen auf die Daten dort hinein. Alle weitere Logik musst Du dann auf diese minimale Menge von Zugriffsfunktionen abstützen und die kann dann woanders liegen.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Aber warum ist das überhaupt so problematisch? Heutige Rechner haben doch so viel Speicher, dass man sich um ein bisschen zusätzlichen Code nicht wirklich Gedanken machen muss.
Ein echtes Problem ist das bei 16GiB Speicher wohl nicht. Aber ich bin mit dem Z80 groß geworden. Daher habe ich bei so einer unsauberen Vorgehensweise immer ein schlechtes Gewissen. Aber es geht ja auch darum, zu verstehen was geht und wo die Grenzen sind. Ich muss mal testen, wie groß der Unterschied überhaupt ist und wie hoch der Aufwand für die Umstellung ist (ich muss das 3 Mal machen). Was bei C definitiv fehlt sind Namensräume.
|
snafu1
Anmeldungsdatum: 5. September 2007
Beiträge: 2123
Wohnort: Gelsenkirchen
|
Das Problem der fehlenden Namensräume löst man in C bekanntlich mit Präfixen, indem man den Bibliotheksnamen (oder ein griffiges Kürzel) und ggf noch die Bezeichnung des "Moduls" vor den eigentlichen Funktionsnamen setzt. Das führt zwar zu längeren Namen und ist kein vollständiger Ersatz für "echte" Namensräume, aber es vermindert zumindest das Risiko von Kollisionen mit anderen Bibliotheken. Und bezüglich deines ursprünglichen Problems: Wenn du für verschiedene Anwendungsfälle unterschiedliche APIs benötigst, also z.B. abgespeckte Varianten, dann lässt sich das über die Header-Dateien lösen. Entweder indem du für jede Variante eine eigene Header-Datei schreibst oder indem du Präprozessor-Anweisungen schreibst, damit nur eingebunden wird, was im jeweiligen Szenario sinnvoll erscheint.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Das Problem der fehlenden Namensräume löst man in C bekanntlich mit Präfixen,...
Das habe ich ansatzweise auch schon so gemacht. Hier handelt es sich teilweise um Programmcode, dessen Ursprung über 10 Jahre alt ist und noch aus meinem Windows Leben stammt. Damals war ich noch sehr schreibfaul und die Programme waren noch nicht sehr umfangreich.
Und bezüglich deines ursprünglichen Problems: Wenn du für verschiedene Anwendungsfälle unterschiedliche APIs benötigst, also z.B. abgespeckte Varianten, dann lässt sich das über die Header-Dateien lösen.
Das sehe ich jetzt nicht ganz so. Auf Quelltextbasis habe ich das schon angewendet, z.B. indem ich in einer globalen config.h oder global.h entsprechede Makros definiere. Auf Lib Ebene geht das wohl nicht. Ich poste mal eine der Header Dateien dazu:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | /*-----------------------------------------------------------------------------
** jpg_com.h
** 2019-07-24 redisign of jcomtool.c
*/
#ifndef JPG_COM_H
#define JPG_COM_H
typedef struct {
char identifier[5];
char majorID;
char minorID;
char units;
unsigned char Xdensity[2];
unsigned char Ydensity[2];
unsigned char Xthumb;
unsigned char Ythumb;
} JfifHeader;
#ifdef __cplusplus
extern "C" {
#endif
int jpg_get_com( const char * name, char * cbuf, int len, int no_nl );
int jpg_put_com( char * name, char * cbuf );
#ifdef __cplusplus
}
#endif
#endif
|
Programme die nur etwas anzeigen wollen benötigen kein *put*. Das ganz gibt es dann auch, in erweiterter Version, für PNG und GIF.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Ich habe mir mal die Mühe gemacht, wenigstens ungefähr, herauszufinden wie groß oder klein die Einsparung ist. Dabei gehe ich von den *.o Dateien aus (alle Angaben in Bytes).
Da ist dann wohl noch viel Overhead dabei. Jedenfalls sagt readelf dass das Text Segment für die Komplettversion 2351 Byte lang ist. Das sollte auch ein Raspi schaffen.
|
Neral
Anmeldungsdatum: 3. Oktober 2007
Beiträge: 229
|
Wenn du unbedingt auf Codegröße optimieren willst (bei einem Mikrocontroller mit 8 KiB Flash mag das sinnvoll sein, bei einem Pi nur bedingt), dann gibt es -ffunction-sections und -Wl,--gc-sections , mit dem der Linker unbenutzte Funktionen aus dem Executable schmeißen kann. Doku hier (Strg-F -ffunction-sections), sagt aber auch, dass man das nur machen soll, wenn man signifikant davon profitiert, weil das auch einige Optimierungen behindert.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Danke für die Hinweise. Finde ich sehr interessant und trickreich. Aber beim Parameter "-Wl" bin ich etwas unsicher. Wenn ich den Zusammenhang richtig verstanden habe, würde ich den in einem Makefile mit getrenntem Linker Aufruf wohl nicht benötigen. Auf der anderen Seite denke ich, das dies in meinem aktuellen Fall nicht viel bringen würde, da ich recht viele kleine Funktionen habe. Wenn dann jede Funktion in ein eigenes Segment gepackt wird, und dann alles über 64-Bit Adressen abgewickelt werden muss, würde das die Einsparung teilweise wieder aufheben. In dem Zusammenhang meine ich mich zu erinnern, das es damals im MC68000 Assembler extra Anweisungen für die Segment Zuordnung gab. Ob das jetzt aber etwas vergleichbares war, kann ich nicht sagen (ist zu lange her). Jedenfalls werde ich die Module nochmal überarbeiten um den Unterschied zwischen Lesen und Schreiben zu verringern (die Initialisierung ist fast gleich).
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Ich würde ja sagen: Ausprobieren, Messen und Berichten. ☺
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Ok, erster Zwischenbericht. Ich habe von 3 möglichen Modulen aber nur eines optimiert. Ich beschreibe das mal komplett, damit erkennbar wird, ob mir irgendwo einen Denkfehler unterlaufen ist. Das Testprogramm wird normalerweise so gebaut:
gcc -Wall -o sdir sdir.c jpg_com.o png_com.o GIF_com.o
jpg_com.o ist dann 8920 Bytes groß und die resultierende Binärdatei 31072 (stripped). Dann habe ich jpg_com.o neu erzeugt.
gcc -c -ffunction-sections jpg_com.c
Die neue Größe ist dann 11584 Bytes. Anschließend neu zusammengesetzt mit
gcc -Wl,--gc-sections -o sdir sdir.o jpg_com.o png_com.o GIF_com.o
Die neue Datei ist dann nur noch 31040 Bytes groß. Wie davon die Geschwindigkeit beeinflusst wird, kann ich noch nicht sagen. Dazu müsste ich das Programm auf meinen echten Datenbestand (ca. 17000 Dateien) auf einer externen USB Platte loslassen. Und da weiß ich, dass USB die Bremse ist. Wenn dann die Gesamtlaufzeit weiterhin bei 6 Minuten bleibt, kann ich keine Aussage machen.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Beim Compilieren/Linken sind aber Größe und Geschwindigkeit konfligierende Ziele, das weißt Du schon? Loopunrolling beispielsweise kann den Code aufblähen, aber er ist dann einen Tick schneller. Oder Code, der an mehreren Stellen vorkommt, kann identifiziert werden und an allen Stellen bis auf eine wegoptimiert werden - von den anderen Stellen muss dann aber dahingesprungen und wieder zurückgesprungen werden. Etwas kleinerer Code, aber langsamer.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Ja, das ist bekannt. Und Geschwindigkeit war auch der Auslöser für mein Programm. Ich hatte da eher die Bemerkungen in der von Neral verlinkten Doku im Kopf. Da wird in etwa gesagt, dass einige einfache Optimierungen verunmöglicht werden, weil dann z.B. keine kurzen Adressen verwendet werden können, wenn das Ziel in einem anderen Segment liegt. Aber wie man sieht, bringt das zumindest auf einem 64-Bit System keine wirklichen Einsparungen, zumindest nicht, wenn bei vielen kurzen Funktionen nur wenige wegfallen. Nachtrag: Den Zeittest habe ich leider versemmelt, weil ich die falsche Ausgangszeit im Kopf hatte. Daher war ich nicht darauf vorbereitet, das die Erfassung schon nach ca. 3 Minuten erfolgt war und hatte auf der Stoppuhr den falsche Knopf gedrückt. Die Wiederholung des Tests haben mir leider die 16GiB Arbeitsspeicher verdorben. Das war dann in ca. 2.5 Sec erledigt. Ich denke dieses Scenario ist für Speedtests ungeeignet.
|