Viele Funktionen


  • Mod

    Die Frage passt nicht zu dem, wie ein Computerprogramm funktioniert. Der Code des Programms steht in der Executable, der wird beim Programmstart einmalig so in den Speicher geladen, wie er da drin steht. Da wird nichts vervielfältigt, wenn die Funktion aufgerufen wird. Ein Funktionsaufruf bedeutet stattdessen, dass dem Prozessor die Adresse gegeben wird, wo der Code der Funktion steht, und gesagt wird "mach mal ab da weiter!". Dann nimmt der Code der Funktion sich selber das was er als lokale Ressourcen braucht oder was er als Parameter übergeben bekommen hat (normalerweise ist per Konvention klar, wo er sich was nehmen darf, aber theoretisch könnte er das auch vom Aufrufer gesagt bekommen). Ein Teil der Parameter ist auch eine Rücksprungadresse zum Aufrufer und das Ende einer Funktion heißt, dass dem Prozessor wieder gesagt wird, er soll ab da weiter machen.

    Jedenfalls ist das wichtigste, dass der Funktionsaufruf nicht irgendwie Codekopien erzeugt. Aber es kann Speicher für lokale Ressourcen verbraucht werden, wobei dies auch unsichtbare Ressourcen wie Rücksprungadressen beinhalten kann. Tausende Funktionsaufrufe nacheinander sind dabei gar kein Problem, da man ja einfach wieder die gleichen Ressourcen belegen kann wie die vorherige Funktion. Tausende Funktionsaufrufe "tief" (d.h. eine Funktion ruft eine zweite auf, die eine dritte aufruft, etc.) kann hingegen schnell ein Problem werden, oft schon nach wenigen hundert Ebenen. Das ist dann der berühmte "Stackoverflow". Das ist aber ein sehr ungewöhnliches Pattern und passiert eigentlich nur bei groben Programmierfehlern. Bei normalen Programmen wird das nie vorkommen und man braucht sich um so etwas nicht zu sorgen.

    Für (sehr technische) Details, siehe:
    https://en.wikipedia.org/wiki/Calling_convention
    https://en.wikipedia.org/wiki/Call_stack



  • Allzu technisch möchte ich jetzt nicht werden, weswegen ich jetzt mal kurz ein Beispiel bringe:

    Ich erzeuge in etwa solchen Code:

    f_1(double *x)
    { 
      return 3*x[1] + 2* x[2];
    }
    
    f_2(double *x)
    {
      return -0.5 *x[1] +2*x[2] -5*x[7];
    }
    
    ...
    
    f_10000(double *x)
    {
     ...
    }
    

    Also jede Menge kleine Berechnungen.

    Aufgerufen wird dann nacheinander:

    for(int i = 0; i < 10000; i++)
    {
      //fp enthält die pointer auf die f-Funktionen
       fp[i](x)
    }
    

    Ist so etwas in irgendeiner Form problematisch?





  • @Schlangenmensch

    Ist auch das Verwenden der Sprungtabelle fp unproblematisch? Immerhin sind da ja uU 10Tausende Funktionsadressen gespeichert.


  • Mod

    Kleine Funktionen: Super! So geht das richtig!
    Technische Probleme: Keine.
    Durchsteppen durch ein Array von Funktionszeigern: Wartungsalptraum!


  • Mod

    @ravenheart_ggg sagte in Viele Funktionen:

    @Schlangenmensch

    Ist auch das Verwenden der Sprungtabelle fp unproblematisch? Immerhin sind da ja uU 10Tausende Funktionsadressen gespeichert.

    Also so ungefähr 10000 * 64 Bit? Wie viel Speicher hat dein Computer?

    Wie mein vorheriger Post schon ausdrücken wollte: Hier gibt es kein Problem technischer Art, aber fast sicher ein Problem logischer Art. 10,000 verschiedene Funktionen zu haben klingt nach einem extremen Abstraktionsfehler.



  • @SeppJ sagte in Viele Funktionen:

    10000 * 64 Bit

    Die 10000 ist jetzt für ein kleineres Beispiel. Die Anzahl der Funktionen, die ich generieren könnte, ist praktisch unbegrenzt.

    Die Wartung ist nicht von Bedeutung, da der finale Code nicht manuell bearbeitet wird. Der gesamte Quellcode wird automatisch erzeugt.



  • SeppJ schrieb ja schon, dass jeder Eintrag in deine Tabelle 64 Bit groß ist. Wenn wir annehmen, dass du 3GB RAM über hast, kannst du: 3000000000*8/64 = 375000000 Einträge haben. Das sind echt viele Funktionen die du da nacheinander aufrufen könntest.


  • Mod

    In dem Fall wird irgendwann auch relevant, dass jede Funktion auch einen gewissen Platz in der Executable braucht, und dass dieser wahrscheinlich auch nicht wegoptimiert werden kann. Und dann hat man tatsächlich ein Problem technischer Art.

    Für mich klingt das immer noch nach einem Wartungsalptraum, weil hier ein Problem, dass man normalerweise vergleichsweise einfach zur Laufzeit lösen würde, auf die Compilezeit verlagert hat. Vereinfacht gesagt wäre es doch grob damit getan, nur eine Funktion zu haben, nämlich so etwas ähnliches wie die Generatorfunktion, und diese zur Laufzeit mit den entsprechenden Parametern aufzurufen. Dann hat man weder technische noch Abstraktionsprobleme.



  • eigentlich solltest du die funktionen in kategorien unterteilen und dann erst die hauptfunktion und danach die unterfunktionen aufrufen. ähnlich solltest du übrigens mit datensturkturen für parameter und rechenergebnisse verfahren.


Log in to reply