Standardfunktion ersetzen



  • Hallo,

    gibt es die Moeglichkeit memcpy() durch myMemCpy() zu ersetzen, ohne alle Aufrufe zu ändern?

    Geht das hier, bzw. wenn es geht, wie schön haltet ihr das?
    Dreckige oder schöne Lösung?

    #define memcpy(blbla) myMemCpy()
    


  • olpo schrieb:

    wie schön haltet ihr das?

    Naja, gute Ideen sehen andern aus. Würde mich aber interessieren, wofür du das brauchst.

    #define memcpy(dest, source, num) myMemCpy(dest, source, num)
    // Bzw, wenn die Argumente eh gleich sind:
    #define memcpy myMemCpy
    


  • @Singender Holzkübel: wenn du in deinem Programm eine Standardfunktion verwendest, die einen Algorithmus verwendet, der um einiges langsamer ist als deine Funktion, die explizit auf die Eigenheiten deines Programms ausgelegt sind (beispielsweise ein memmem , welches keine naive Byte-für-Byte-Vergleiche macht, oder ein sprintf, welches nicht entsetzlich langsam sein soll, wenn du nur zwei oder drei Zahlen schreiben willst), dann ergibt das schon Sinn. Guter Stil ist aber was anderes.

    @TE: Was hält dich davon ab, ein globales Suchen&Ersetzen durchzuführen?

    Wenn du unter Linux bist, kannst du mit LD_PRELOAD eine dynamische Bibliothek angeben, aus der zuerst die Symbole geladen werden sollen, die du verwenden willst. Damit kannst du bspw. deine eigene Speicherverwaltung in ein bestehendes Programm einfügen und kannst dann sehen, ob das besser oder schlechter performt. Aber woanders als unter glibc habe ich das bisher noch nicht gemacht, und hat eh nichts mit dem C-Standard zu tun. Außerdem ist das Manipulation auf Linkebene, nicht auf Compilier- oder Präprozessorebene.

    Ansonsten ... @Holzkübel: +1.


  • Mod

    dachschaden schrieb:

    Wenn du unter Linux bist, kannst du mit LD_PRELOAD eine dynamische Bibliothek angeben, aus der zuerst die Symbole geladen werden sollen, die du verwenden willst. Damit kannst du bspw. deine eigene Speicherverwaltung in ein bestehendes Programm einfügen und kannst dann sehen, ob das besser oder schlechter performt. Aber woanders als unter glibc habe ich das bisher noch nicht gemacht, und hat eh nichts mit dem C-Standard zu tun. Außerdem ist das Manipulation auf Linkebene, nicht auf Compilier- oder Präprozessorebene.

    Wenn man's drauf anlegt, dann geht das sicher auch auf Compiler-/Linkerebene. Dem Compiler verbieten, Intrinsics zu benutzen, dann beim Linken dem Linker eine Bibliothek mit den eigenen Standardfunktionen und einer höheren Priorität als die Symbole aus der Standardbibliothek geben.

    Wie das genau geht, kommt auf das genaue Compiler-/Linkergespann an. Aber dort findet man eher Unterstützung für solche Späßchen als wenn man versucht das auf Ebene der Sprache (also defines und ähnliches) zu machen. LD_PRELOAD ist auch ein guter Tipp, das ist ja quasi auch die Linkerebene. Jedenfalls würde ich allgemein eher auf dieser Ebene gucken.



  • SeppJ schrieb:

    Wenn man's drauf anlegt, dann geht das sicher auch auf Compiler-/Linkerebene. Dem Compiler verbieten, Intrinsics zu benutzen, dann beim Linken dem Linker eine Bibliothek mit den eigenen Standardfunktionen und einer höheren Priorität als die Symbole aus der Standardbibliothek geben.

    Sollte ein Compiler nicht eh (außer bei abnorm hohem Optimierungslevel - bei dem ich schon die eine oder andere Warnung für vom Compiler generiertem Code bekommen habe :D) nicht eh immer alles in eine eigene Funktion packen, wenn diese nicht in irgendeiner Art und Weise als inline deklariert wurde?

    Es ist zwar ziemlich untypisch, aber theoretisch könntest du ja auch manuell ein executable binary in den Speicher mappen und dir einen Funktionspointer auf die Funktion holen, die du ausführen willst. Ui, das hörst sich so verführerisch an, das werde ich die nächsten Tage mal ausprobieren. 🙂 ELF garantiert meines Wissens zum Beispiel, dass Funktionsaufrufe auch von libc-internen Funktionen (pthreads, strdup, opendir usw.) auf die vorher eingebundenen Symbole umgeleitet werden. Wie willst du solche Symbole überschreiben, wenn der Code inlined wurde? Ich meine, der ist dann ja schon DRIN, da wird nicht aus der jeweiligen Funktion rausgesprungen, sondern der wird einfach ausgeführt. Oder wird bei derartigen Optimierungen auf diese Garantie geschissen? Oder war das auf die Fälle bezogen, wo man Link Time Optimizations aktivieren könnte?

    SeppJ schrieb:

    LD_PRELOAD ist auch ein guter Tipp, das ist ja quasi auch die Linkerebene. Jedenfalls würde ich allgemein eher auf dieser Ebene gucken.

    Ja, aber nicht jedes System hat Unterstützung dafür, nicht wahr? 😉 Auf Sprachebene ist es wahrscheinlicher, dass es überall funktioniert, aber schön ist es nicht gerade. Und man muss sich auch nicht mit der Binärpräsentation des Codes rumschlagen, das übernehmen Compiler und Linker schon für einen.



  • 'Standardfunktion ersetzen' ist generell eine schlechte Idee,
    #define dafür zu benutzen, erst recht.


  • Mod

    dachschaden schrieb:

    SeppJ schrieb:

    Wenn man's drauf anlegt, dann geht das sicher auch auf Compiler-/Linkerebene. Dem Compiler verbieten, Intrinsics zu benutzen, dann beim Linken dem Linker eine Bibliothek mit den eigenen Standardfunktionen und einer höheren Priorität als die Symbole aus der Standardbibliothek geben.

    Sollte ein Compiler nicht eh (außer bei abnorm hohem Optimierungslevel - bei dem ich schon die eine oder andere Warnung für vom Compiler generiertem Code bekommen habe :D) nicht eh immer alles in eine eigene Funktion packen, wenn diese nicht in irgendeiner Art und Weise als inline deklariert wurde?

    Das Inlining von Standardfunktionen ist ziemlich normal. Typisches Beispiel zum Beispiel der GCC mit mathematischen Funktionen, wo bei statischen Werten direkt das Ergebnis eingesetzt wird, man die Mathebibliothek nicht einmal mehr braucht. Andere Sachen, die gerne und frühzeitig geinlined werden sind performcancekritische, einfache Funktionen, also memcpy und vergleichbares. Genau, wonach der TE fragt. Auch schon oft gesehen sind diverse Optimierungen bei Ausgabefunktionen, wie printf("%s\n", s) zu puts(s) (wobei der puts-Aufruf aber natürlich nicht geinlined wird) und ähnliches.

    Wutz schrieb:

    'Standardfunktion ersetzen' ist generell eine schlechte Idee,

    Warum? Die sind doch nicht heilig. Sind Funktionen wie alle anderen auch.



  • SeppJ schrieb:

    Das Inlining von Standardfunktionen ist ziemlich normal. Typisches Beispiel zum Beispiel der GCC mit mathematischen Funktionen, wo bei statischen Werten direkt das Ergebnis eingesetzt wird, man die Mathebibliothek nicht einmal mehr braucht. Andere Sachen, die gerne und frühzeitig geinlined werden sind performcancekritische, einfache Funktionen, also memcpy und vergleichbares. Genau, wonach der TE fragt. Auch schon oft gesehen sind diverse Optimierungen bei Ausgabefunktionen, wie printf("%s\n", s) zu puts(s) (wobei der puts-Aufruf aber natürlich nicht geinlined wird) und ähnliches.

    Ja gut, dann verstehe ich aber deinen Einwand nicht. Dass solche Sachen optimiert werden, wo der Compiler mit sehr hoher Wahrscheinlichkeit einen statischen Wert hinsetzen kann, ist mir schon vorher klar gewesen (siehe bereits erwähnte Compilerwarnungen - hier habe ich bereits öfter den "optimierten" Quellcode noch einmal angesehen, um zu prüfen, was da jetzt eigentlich generiert wurde und wo die Warnung generiert wurde). Aber solche Sachen packt man nun auch wirklich nicht in eine Funktion, und es geht hier ja um Funktionen, die verdeckt werden sollen, nicht um Bruchstücke im Code einer Funktion, wie es bei Inlines der Fall ist.


  • Mod

    dachschaden schrieb:

    Aber solche Sachen packt man nun auch wirklich nicht in eine Funktion, und es geht hier ja um Funktionen, die verdeckt werden sollen, nicht um Bruchstücke im Code einer Funktion, wie es bei Inlines der Fall ist.

    😕

    Noch einmal in einfachen Worten: Der TE will irgendwelche Funktionen aus der Standardbibliothek ersetzen. Die Implementierung kann, darf und wird jedoch bereits bei recht niedrigem Optimierunglevel Funktionsaufrufe aus der Standardbibliothek:
    a) ganz normal inlinen
    oder
    b) durch ganz was anderes ersetzen, so lange es nur das gleiche tut

    In beiden Fällen funktioniert ein Eingriff auf Linkerebene nicht mehr wie gewollt, daher muss man dies verhindern.

    Irgendwie denke ich, dass ich dein

    dachschaden schrieb:

    Sollte ein Compiler nicht eh (außer bei abnorm hohem Optimierungslevel - bei dem ich schon die eine oder andere Warnung für vom Compiler generiertem Code bekommen habe :D) nicht eh immer alles in eine eigene Funktion packen, wenn diese nicht in irgendeiner Art und Weise als inline deklariert wurde?

    falsch verstanden habe, denn meiner Meinung nach macht dein letzter Beitrag wenig Sinn im Kontext. Daher stimmt vermutlich der Kontext nicht. Was genau meinst du mit "alles in eine eigene Funktion packen"?



  • Singender Holzkübel schrieb:

    Würde mich aber interessieren, wofür du das brauchst.

    Auf ARM Prozessoren scheint memcpy() etwas restriktiver gehandhabt zu werden. Man kann da wohl nicht einfach über Array-Grenzen hinwegschreiben.

    char buf[size];
    
    memcpy( &buf[offset], src, 4);
    

    Da im Bsp. 4 Byte größer als das ein-Byte Array-Feld sind, haut ARM einen Error.



  • SeppJ schrieb:

    Irgendwie denke ich, dass ich [deinen Beitrag] falsch verstanden habe, denn meiner Meinung nach macht dein letzter Beitrag wenig Sinn im Kontext. Daher stimmt vermutlich der Kontext nicht. Was genau meinst du mit "alles in eine eigene Funktion packen"?

    Ach, verdammt. Da hatte ich einen Denkfehler. LD_PRELOAD funktioniert meines Wissens nur bei Symbolen, die über dynamische Bibliotheken nachgeladen werden. Dummer Hirnfurz, das.

    Es ging mir darum: wenn ich TUs kompiliere und zusammenlinke, wer garantiert mir, dass sich die Symbole - sprich die Funktionen, deren DEFINITIONEN in den Objektdateien sind - nicht vom Compiler optimiert werden?
    Ich hatte vor einiger Zeit hier nachgefragt, wie man eine dezimale Zahl schnell in einen String schreibt, und wollte dazu die Länge der Zahl in Basis 10 wissen. Als Tipp wurde die Logarithmusfunktion angeben, und die war auch sehr schnell, irgendwas mit 30 Cycles. Aber als ich dann die Berechnung in eine Funktion packte, wollte der Linker die Math-Lib mitdazuhaben, die ich vorher nicht brauchte (weil der Compiler den Call von log2 bereits wegoptimiert hatte). Mit der Bereichnung der Funktion war die Funktion aber dann da und hat 30.000 Cycles gekostet. Der Compiler hätte aber bei ausreichend hoher Optimierungsstufe sagen können, dass er das Ergebnis aller Calls schon mal im Voraus berechnet, die Werte einfügt, und die Funktion komplett rausnimmmt, weil die nur unnötig Ausführungszeit verbraucht. Wenn man dann versucht hätte, mit LD_PRELOAD die Funktion zu ersetzen, wäre nichts bei rumgekommen, weil die Funktion bereits komplett geinlined wurde und nicht mehr gefunden werden kann.

    Natürlich ergibt das keinen Sinn, wenn man von statischem Code wie in Objektdateien oder statischen Bibliotheken ausgeht, LD_PRELOAD funzt wie gesagt bei Funktionen in dynamischen Objekten. In diesem Fall ist das Executable ja bereits mit allen Funktionen bekannt gemacht worden, die dynamisch nachgeladen werden müssen - die also auch von den dynamischen Objekten exportiert werden müsen.

    Sorry. 🙂 Wie gesagt, kurzzeitiger Hirnfurz.



  • olpo schrieb:

    Auf ARM Prozessoren scheint memcpy() etwas restriktiver gehandhabt zu werden. Man kann da wohl nicht einfach über Array-Grenzen hinwegschreiben.

    char buf[size];
    
    memcpy( &buf[offset], src, 4);
    

    Da im Bsp. 4 Byte größer als das ein-Byte Array-Feld sind, haut ARM einen Error.

    Ähhhhh ... habe ich gerade wieder einen Hirnfurz? Über die Bytegrenzen eines Arrays zu schreiben ist generell keine gute Idee, es sei denn, du bist dir GANZ sicher, dass nach dem Wert noch ein definiertes Feld kommt, dass dich davon abhält, in deine Stackdaten zu schreiben.

    OK, ist vielleicht nur eine dumme Vermutung, ich habe keine Ahnung von ARM, aber kann es sein, dass hier kein Redzoning unterstützt wird? Bei Redzoning hast du noch 128 Bytes nach dem eigentlichen Ende des Stacks, die du Beschreiben kannst, da kann das abgefangen werden.

    Siehe auch!



  • dachschaden schrieb:

    Ähhhhh ... habe ich gerade wieder einen Hirnfurz? Über die Bytegrenzen eines Arrays zu schreiben ist generell keine gute Idee, es sei denn, du bist dir GANZ sicher, dass nach dem Wert noch ein definiertes Feld kommt, dass dich davon abhält, in deine Stackdaten zu schreiben.

    Ja, ich finde das auch nicht schön.
    Und das ist legacy code von IBM 😮 .

    dachschaden schrieb:

    OK, ist vielleicht nur eine dumme Vermutung, ich habe keine Ahnung von ARM, aber kann es sein, dass hier kein Redzoning unterstützt wird? Bei Redzoning hast du noch 128 Bytes nach dem eigentlichen Ende des Stacks, die du Beschreiben kannst, da kann das abgefangen werden.

    Siehe auch!

    Weiß ich nicht. Aber da ARM für eingebettete Systeme mit wenig Speicherplatz ist, vermute ich mal, dass das nicht standardmäßig eingefügt wird.

    Egal, was haltet ihr von myMemcpy?
    Irgendwas auszusetzen? Gefährliche Pointer-Sachen, oder alles richtig, schön und sauber?

    void myMemcpy(void *dest,  const void *src, size_t n){
    
    	unsigned int i;
    	uint8_t *destination = (uint8_t*) dest;
    	uint8_t *source = (uint8_t*) src;
    
    	for ( i=0; i<n; i++){
    		*(destination+i) = *(source+i);
    	}
    


  • Erklär noch mal, warum du Standardfunktionen 'ver(schlimm)bessern' oder gar ersetzen willst.



  • @wutz:

    Kuckst du mal hier.

    Und siehst du hier eine Standardreferenzimplementierung.

    Also, ich kann's verstehen. Zwar nicht so, wie olpo das macht (keine Vektorisierung und auch keine Anpassung an die CPU-Architektur), aber der Gedanke generell ist nicht dumm. Und es ist ja nicht so, als ob seine Funktion in einen neuen Standard einfließen wird. 😉


Anmelden zum Antworten