[gelöst] Vector ist wesentlich langsamer als Array?



  • Wenn Dir M[i] Deine Schicht indiziert und M[i][j] Dein Neuron
    brauchst Du also "nur" ein zur Initialisierungszeit dynamisches array...

    -> Das sehe ich noch nicht, aber das muss nichts heißen.
    Also kurz ich weiß nicht, wie ich die eine Dimension reduzieren kann.
    Ich mein, ich könnte mir die Anzahl der Schichten mit der Anzahl der Neuronen
    zwischen-speichern und somit die "Referenzierung" auf ein Index selber zusammen basteln, aber ich weiß nicht ob das gemeint ist.

    Dein Neuron hat welche Parameter?

    typedef struct _neuron
    {
    double* weight; // gewichte der eingänge
    
    LPVOID _ue_func; //zeiger auf eine Gewichtungsfunktion (übertragungsfunktion)
    LPVOID _outbarrier_func;// Ausgangsschwellenfunktion, also aktivierungsfunktion
    }
    

    oder sind die Gewichte bei Dir schon mit M[i][j][w] gegeben?

    Na ja nicht ganz... ein Neuron hat soviele Gewichte wie Neuronen in der vorhergehenden Schicht. Also jede Verbindung von Neuron j aus Schicht i
    führt zu Neuron k in Schicht i+1. Die Gewichte sind in w[i][j][k] gespeichert.
    wTx in activate liefert mir somit die Aktivierung durch die Vorgänger-Neuronen und der tanh(beta * (wTx - bias)) bestimmt somit die Weiterleitung in die Nächste Schicht. Diese wird in M[i+1][k] gespeichert.

    Welche Funktionen LPVOID ... übernehmen sollen kann ich erahnen, sehe aber nicht die Notwendigkeit.



  • Ich werde jetzt bis morgen früh erst mal das Feld räumen.
    Sollten noch Fragen an mich kommen also nicht warten. Ich
    werde heut nicht mehr antworten.
    Um aber vielleicht ein besseres Bild von solch einem Netz
    zu schaffen wollte ich nur ebenmal eine graphische Darstellung
    posten. Auf dieser Seite wäre solch ein Netz, was eigentlich
    in meinem Code implementiert wurde. Text der Seite hab ich nicht
    mehr gelesen nur die Darstellung ist denke ich hilfreich.

    http://www.google.de/imgres?imgurl=http://193.25.32.158/fb2/prof/skrueger/images/stories/neuronetze/rendite.gif&imgrefurl=http://www.kolleg.wi.hs-anhalt.de/fb2/prof/skrueger/index.php%3Foption%3Dcom_content%26view%3Darticle%26id%3D72%26Itemid%3D85&usg=__Wf5XkEsDvPGlCUYNBuxG8SVVb9k=&h=226&w=462&sz=26&hl=de&start=0&zoom=1&tbnid=Y-uU2AeUVBalsM:&tbnh=88&tbnw=179&ei=fwcjT67FA86Rswbkt5yTDw&prev=/search%3Fq%3Dk%C3%BCnstliches%2Bneuronales%2Bnetz%26um%3D1%26hl%3Dde%26sa%3DN%26biw%3D1920%26bih%3D917%26tbm%3Disch&um=1&itbs=1&iact=rc&dur=176&sig=107543848169332014707&page=1&ndsp=45&ved=1t:429,r:14,s:0&tx=74&ty=42

    Hier das Bild isoliert:
    http://193.25.32.158/fb2/prof/skrueger/images/stories/neuronetze/rendite.gif



  • Ach schade,.. bin gerade fertig geworden,...

    Naja vlt können die anderen das ja mal auseinander nehmen:

    //#######################################################################################################
    // Base Array class deklaration
    //#######################################################################################################
    
    template<typename T>
    class _base_array
    {
    private:
    	T *_ptr;
    	unsigned int size;
    
    	bool _malloc(unsigned int Size);
    	void _free(void);
    	void _clear_all(void);
    public:
    	_base_array();
    	_base_array(unsigned int Size);
    	~_base_array();
    	void reserve(unsigned int Size); // just call it ONCE
    	unsigned int _nr_of_elements(void);	// returns the absolute number of accessable elements
    	unsigned int _size(void);	//returns the size of array in Bytes
    	T & operator[](int Index);
    };
    
    //#######################################################################################################
    // Base Array class definitions
    //#######################################################################################################
    template<typename T>
    bool _base_array<T>::_malloc(unsigned int Size)
    {
    	bool bRet=false;
    	if(	this->_ptr ) return bRet;
    
    	this->_ptr=reinterpret_cast<T*>(malloc(sizeof(T)*Size));
    	if(	!this->_ptr ) 
    	{
    		this->size=0;
    		return bRet;
    	};
    
    	this->size=Size;
    
    	for(int i=0; i<Size; i++)
    		(*(this->_ptr+i))=T();
    
    return !bRet;
    };
    
    template<typename T>
    void _base_array<T>::_free()
    {
    if(!this->_ptr)
    {
    	this->size=0;
    	return;
    };
    
    free(reinterpret_cast<void*>(this->_ptr));
    
    this->_ptr=reinterpret_cast<T*>(0);
    this->size=0;
    };
    template<typename T>
    void _base_array<T>::reserve(unsigned int Size)
    {
    	this->_malloc(Size);
    };
    
    template<typename T>
    void _base_array<T>::_clear_all()
    {
    	this->size=0;
    	this->_ptr=reinterpret_cast<T*>(0);
    };
    
    template<typename T>
    _base_array<T>::~_base_array()
    {
    	this->_free();
    	this->_clear_all();
    };
    
    template<typename T>
    _base_array<T>::_base_array()
    {
    	this->_clear_all();
    };
    
    template<typename T>
    _base_array<T>::_base_array(unsigned int Size)
    {
    	this->_clear_all();
    
    	this->_malloc(Size);
    };
    
    template<typename T>
    unsigned int _base_array<T>::_nr_of_elements()
    {
    	return this->size;
    };
    
    template<typename T>
    unsigned int _base_array<T>::_size()
    {
    	return this->size*sizeof(T);
    };
    
    template<typename T>
    T & _base_array<T>::operator [](int Index)
    {
    	if((Index <0 )|| (static_cast<unsigned int>(Index) > this->size-1)) 
    		return *(new T());
    
    	return *(this->_ptr +Index);
    };
    //#######################################################################################################
    
    typedef _base_array<int> _bai;
    typedef _base_array<_bai> _cai;
    typedef _base_array<_cai> _dai;
    
    //#######################################################################################################
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	_bai b(12);
    	_cai c(12);
    	_dai d;
    	d.reserve(12);
    
    	for(int i=0; i<12; i++)
    	{
    		b[i]=i;
    		c[i].reserve(12);
    		d[i].reserve(12);
    	};
    
    	for(int x=0; x<12; x++)
    	{
    		for(int y=0; y<12; y++)
    		{
    			d[x][y].reserve(12);
    
    		}
    	};
    
    	for(int x=0; x<12; x++)
    	{
    		for(int y=0; y<12; y++)
    		{
    			for(int z=0; z<12; z++)
    			{
    				d[x][y][z]=x+y*12+z*12*12; //weiß nicht ob die rechnung richtig ist,...
    			}
    		}
    	};
    
    	__debugbreak();
    
    	return 0;
    }
    

    grüße



  • Warum benennst du deine Klassen so grausam mit dem _ als Präfix?



  • Ja,..
    Warum machen andere das nicht?
    _Weil _man _die _Freiheit _hat das zu tun und zu lassen was man will,..
    ... und ausserdem ließt und schreibt sich das für mich leichter,...
    Ansonste fällt mir nicht mehr ein..



  • zeusosc schrieb:

    Warum machen andere das nicht?

    Weil alle Bezeichner vom Standard reserviert sind, die mit einem Unterstrich starten auf welchen ein Großbuchstabe folgt. Ok, deine Bezeichner sind zwar nicht nicht standardkonform, aber doch mehr als unüblich. Vor allem für Klassen und Funktionen. Da sind insgesamt noch so einige Hauer drin, aber da will ich jetzt nicht drauf eingehen, irgendjemand nimmt sich bestimmt die Zeit. 😉



  • Ok,..

    soweit ich weiss folgt bei mir auf den Unterstrich aber ein
    kleiner Buchstabe bzw. eine Zahl.
    Daher unterscheidet sich das von dem reservierten,..

    Hauerdrinn?

    jetzt bin ich neugierig,... auslernen will ich ja auch nicht,...



  • wow... Danke!
    Ich muss mich da erstmal reinlesen. Diesen "erfahrenen"
    Programmierstil bin ich noch nicht gewöhnt. Aber ich
    glaube zu sehen was gemeint ist. Werde einfach meinen
    Code überarbeiten (angelehnt an das gepostete).

    Vielen Dank an alle die Tipps gegeben haben. Ich
    werde jetzt einfach mal das Thema als gelöst markieren.



  • zeusosc schrieb:

    Hauerdrinn?

    jetzt bin ich neugierig,... auslernen will ich ja auch nicht,...

    Keine Sorge, "Auslernen" wirst du so schnell sicher nicht. 😉

    - Die sehr hässlichen _ am Anfang wurden ja schon erwähnt.
    - Ein komisches selbst gebasteltes malloc statt new[]? 😮
    - Cast nach void*?
    - Cast von 0 nach T*?
    - Das ständige this-> irritiert total.
    - Nicht richtig eingerückt.
    - Komische und unnötige ; hinter if() und hinter Funktionen, die den Lesefluss total behindern.
    - Index von operator [] nicht unsigned.

    Und wo bitte ist bei dir überhaupt der Vorteil zu std::vector? Im Speicher liegt doch immer noch alles kreuz und quer, oder habe ich in dem WirrWarr jetzt den Super-Trick übersehen?



  • @ComputerCarl

    Was du noch machen kannst, wenn die Dimensionen zwar fest aber erst zur Laufzeit bekannt sind, ist, dir eine Klasse zu bauen, die Größenangaben zu den Dimensionen im Konstruktor nimmt, und dann Zugriff über operator () mit drei Indexvariablen auf einmal gibt. (Intern nutzt du einfach einen std::vector und reservierst sx * sy * sz Elemente am Stück).

    int main()
    {
      unsigned sx, sy, sz; // Werte einlesen
      DeineKlasse<float> mat(sx, sy, sz);
      mat(5, 7, 3) = 77; // Zugriff
    }
    

    Ob das viel schneller ist weiß ich nicht, da du den Index halt auch zur Laufzeit ausrechnen musst. (Das sind halt Multiplikationen, wie man die Dimensionen runterrechnet weißt du?)
    Aber ich denke mal einen Versuch ist es wert.


  • Mod

    cooky451 schrieb:

    Ob das viel schneller ist weiß ich nicht, da du den Index halt auch zur Laufzeit ausrechnen musst. (Das sind halt Multiplikationen, wie man die Dimensionen runterrechnet weißt du?)
    Aber ich denke mal einen Versuch ist es wert.

    Glaub mir, das ist VIEL ⚠ ⚠ ⚠ schneller.

    Was ist denn Arraydereferenzierung? Additionen und Multiplikationen. Es ist daher so schnell wie ein zusammenhängendes Array mit dem Komfort eines Containers.



  • SeppJ schrieb:

    Was ist denn Arraydereferenzierung? Additionen und Multiplikationen. Es ist daher so schnell wie ein zusammenhängendes Array mit dem Komfort eines Containers.

    Äh ja, stimmt, der Index muss ja auch bei statischen Arrays errechnet werden, irgendwie hatte ich da einen Denkfehler. Super, dann dürfte das ja die Lösung sein.



  • zeusosc schrieb:

    soweit ich weiss folgt bei mir auf den Unterstrich aber ein
    kleiner Buchstabe bzw. eine Zahl.

    17.6.4.3.2: Global names, zweiter Punkt:

    Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

    Deine Klasse liegt im global namespace, deshalb ist ALLES was mit einem einzigen Unterstrich beginnt, ebenso der Implementierung vorbehalten und sollte vom User nicht verwendet werden.



  • cooky451 schrieb:

    zeusosc schrieb:

    Hauerdrinn?

    jetzt bin ich neugierig,... auslernen will ich ja auch nicht,...

    Keine Sorge, "Auslernen" wirst du so schnell sicher nicht. 😉

    - Die sehr hässlichen _ am Anfang wurden ja schon erwähnt.
    - Ein komisches selbst gebasteltes malloc statt new[]? 😮
    - Cast nach void*?
    - Cast von 0 nach T*?
    - Das ständige this-> irritiert total.
    - Nicht richtig eingerückt.
    - Komische und unnötige ; hinter if() und hinter Funktionen, die den Lesefluss total behindern.
    - Index von operator [] nicht unsigned.

    Und wo bitte ist bei dir überhaupt der Vorteil zu std::vector? Im Speicher liegt doch immer noch alles kreuz und quer, oder habe ich in dem WirrWarr jetzt den Super-Trick übersehen?

    Also, trick ist da keiner.
    Die Anforderung war (so wie ich das sah):
    Schreibe ein zur initialisierungszeit dynamisches Array auf
    das man mit [][][] zugreifen kann, und recht fix ist.

    STL vectoren springen häufig durch verschiedene Unterfunktionen,
    die die Länge prüfen, ob der Zeiger richtig ist und und und,..
    hier wird alloziiert und über maximal zwei ptrs beim zugriff
    dereferenziert (und auf das dritte dan referenziert)...
    sollte also relativ flink laufen,..

    Jetzt mal vom underscore abgesehen:

    * Zum new: ich vermeide es wo ich kann! (hier nutze ich es exakt 1x)
    Wird der new operator nicht über den source definiert, so findet
    eine (Compiler spezifische) Ersetzung statt, die ich erst zur Laufzeit
    sehe. Das will ich nicht!

    * Cast von 0 nach T* : 0 ist kein Typ.
    Sind verschiedene konvertierungsvorschrifften vorhanden,
    wird die erste passende vom Compiler genommen. (Meistens basistypen).
    Ich will das der Compiler "explizit" 0 als T* behandelt.

    * Cast nach void*: sehe ich jetzt nicht,.. wo?

    * Index operator [] nicht unsigned: Ja,.. kann man so und so sehen,
    "ist übergebene uint > size dann gib referenz" wäre hier wohl
    bessere wahl gewesen, da hast du recht...

    * this-> bleibt definitiv !!! es ist ein unterschied ob ich eine
    Methode der Instanz aufrufe oder eine gleichnamige Funktion aus
    dem gleichen Namensraum, geschweige denn Member.
    Da der Compiler das so oder so die addy's der Member aus der
    Referenzierung des this holt (in asm schön sichtbar), dann
    kann ich es auch gleich richtig machen. Ausserdem ist dann
    IMMER klar, WANN ich eine Methode oder Member innerhalb einer
    Methode aufrufe,...
    (Macht sich besonders bemerkbar wenn intellisense spinnt)

    * Einrückung: hä? wie was? wo?
    Es gibt ja mal wohl mehrere Varianten von einrückungen...

    * ";" ?? Meines wissens hat nach jeder abgeschlossenen Anweisung ein ";"
    zu folgen. Auch wenn manchen Compilern das wurscht ist....

    Also bis auf die underscores und dem uint (danke, ja da haste ja recht)
    sehe ich jetzt nicht viele hauerdrinn,...

    @Cookie457:
    Ja, hatte ich auch zuerst überlegt,... dann dachte ich, bevor er sich
    mit "suchen und ersetzen" menüpunkt anlegt um die [i][j][k] durch
    (i,j,k) zu ersetzen, gleich n template mit variabler dim anzahl
    basteln. Er nutzt ja mehrere 1d 2d und 3d arrays.,...

    grüße


  • Mod

    zeusosc schrieb:

    STL vectoren springen häufig durch verschiedene Unterfunktionen,
    die die Länge prüfen, ob der Zeiger richtig ist und und und,..

    Wenn du mit "häufig" meinst "im Debugmodus", dann kann man das so stehen lassen.



  • zeusosc schrieb:

    STL vectoren springen häufig durch verschiedene Unterfunktionen,
    die die Länge prüfen, ob der Zeiger richtig ist und und und,..
    hier wird alloziiert und über maximal zwei ptrs beim zugriff
    dereferenziert (und auf das dritte dan referenziert)...
    sollte also relativ flink laufen,..

    Ein std::vector macht genau das gleiche. (Im Releasemodus). Nein, warte, er ist viel schneller als deine Version, weil er den Index nicht testet.

    zeusosc schrieb:

    * Zum new: ich vermeide es wo ich kann! (hier nutze ich es exakt 1x)
    Wird der new operator nicht über den source definiert, so findet
    eine (Compiler spezifische) Ersetzung statt, die ich erst zur Laufzeit
    sehe. Das will ich nicht!

    Bitte was?

    zeusosc schrieb:

    * Cast von 0 nach T* : 0 ist kein Typ.
    Sind verschiedene konvertierungsvorschrifften vorhanden,
    wird die erste passende vom Compiler genommen. (Meistens basistypen).
    Ich will das der Compiler "explizit" 0 als T* behandelt.

    Ja.. dann nimm nullptr. 😉

    zeusosc schrieb:

    * Cast nach void*: sehe ich jetzt nicht,.. wo?

    In Zeile 58.

    zeusosc schrieb:

    * Index operator [] nicht unsigned: Ja,.. kann man so und so sehen,
    "ist übergebene uint > size dann gib referenz" wäre hier wohl
    bessere wahl gewesen, da hast du recht...

    Nein, die bessere Wahl wäre gewesen, den Test einfach nicht zu machen.

    zeusosc schrieb:

    * this-> bleibt definitiv !!! es ist ein unterschied ob ich eine
    Methode der Instanz aufrufe oder eine gleichnamige Funktion aus
    dem gleichen Namensraum, geschweige denn Member.
    Da der Compiler das so oder so die addy's der Member aus der
    Referenzierung des this holt (in asm schön sichtbar), dann
    kann ich es auch gleich richtig machen.

    Was heißt hier "richtig", es macht halt keinen Unterschied, aber das this wegzulassen ist übersichtlicher.

    zeusosc schrieb:

    Ausserdem ist dann
    IMMER klar, WANN ich eine Methode oder Member innerhalb einer
    Methode aufrufe,...
    (Macht sich besonders bemerkbar wenn intellisense spinnt)

    Das ist auch so klar. Mehr noch, this-> impliziert, dass diese explizite Schreibweise nötig ist, weil sonst etwas Anderes gemeint wäre. Daher sollte man es meiden, wenn dem nicht so ist.

    zeusosc schrieb:

    * Einrückung: hä? wie was? wo?
    Es gibt ja mal wohl mehrere Varianten von einrückungen...

    Das hat nichts mehr mit Stil zu tun, das ist einfach falsch bzw. teilweise gar nicht eingerückt.

    zeusosc schrieb:

    * ";" ?? Meines wissens hat nach jeder abgeschlossenen Anweisung ein ";"
    zu folgen. Auch wenn manchen Compilern das wurscht ist....

    Nein, keinem Compiler ist das egal. Aber leere ; sind jedem Compiler egal, deswegen kompiliert der Code überhaupt. Auf einen Scope muss kein ; folgen. (Und sollte dadurch auch nicht.)

    zeusosc schrieb:

    nicht viele hauerdrinn,...

    Doch, das malloc Gefrickel ist eines der übelsten Sachen die ich seit Langem gesehen habe. Und wenn der Index nicht mehr gültig ist gibt man einfach einen *new T() zurück der nie mehr freigegeben wird? 😮

    zeusosc schrieb:

    gleich n template mit variabler dim anzahl
    basteln. Er nutzt ja mehrere 1d 2d und 3d arrays.,...

    Das funktioniert nicht. Die Dimensionen sind erst zur Laufzeit bekannt, wären sie zur Compilezeit bekannt, könnte man auch einen std::vector<std::array<...>> nehmen. Daher fällt jegliches Templategezauber weg.



  • Ich hoffe, du schaust nochmal in den Thread rein, auch wenn du das als (warum auch immer) gelöst markiert hast.

    Ich habe mal ein wenig Code aus meinem aktuellen Projekt extrahiert und soweit vereinfacht, dass du es mit deinem Kenntnisstand verstehen müsstest. Das ist natürlich so minimal wie möglich, aber es sollte dir schon mal einen deutlichen Speedup geben. Aber du musst noch eine Menge Code hinzufügen, damit das für dein neuronales Netz passt.

    Syntaxfehler könnten drin sein, und eventuell hab ich beim Umbauen auch igendwelche variablen vertauscht. Aber diese Fehler dienen dann nur dazu, dass du versuchst, den Code zu verstehen 😉 (jaja, ich bin gerade zu faul zum testen)

    //stores a matrix in row major format.
    //as minimal as possible, so it offers only element access via op():
    //matrix(i,j)= 10;
    //and a constructor which takes the dimensionality and a method which returns
    //the internal block of memory
    class Matrix{
    private:
        std::vector<float> data;
        std::size_t rows;
        std::size_t columns;
    public:
        Matrix(std::size_t nRows,std::size_t nColumns)
       :data(rows*columns),rows(nRows),columns(nColumns){}
    
       float& operator()(std::size_t row, std::size_t col){
           return data[row*columns+col];
       }
       float operator()(std::size_t row, std::size_t col)const{
           return data[row*columns+col];
       }
       std::size_t numRows()const{
           return rows;
       }
       std::size_t numColumns()const{
           return columns;
       }
       float const* storage()const{
           return &data[0];
       }
    };
    
    //prod(A,b,c) computes c+=A*b
    //where b,c are vectors and A is a matrix 
    void prod(
        Matrix const & matA,
        std::vector<float> const& vecB,
        std::vector<float& vecC,
    ){
    
        std::size_t n = matA.numRows();
        std::size_t k = matA.numColumns();
    
        //calculate 4 rows at the same time until the remaining number of columns is lower than 4. This saves memory accesses by a factor of 4.
        std::size_t maxRow=(n/4)*4;
        std::size_t row=0;
        while(row!= maxRow){
            float* A0 = matA.storage()+row*k;
            float* A1 = A0+k;
            float* A2 = A1+k;
            float* A3 = A2+k;
            float c0=0;
            float c1=0;
            float c2=0;
            float c3=0;
            for(std::size_t i = 0; i != k; ++i){
                c0+=A0[i]*vecB[i];
                c1+=A1[i]*vecB[i];
                c2+=A2[i]*vecB[i];
                c3+=A3[i]*vecB[i];
            }
            vecC[row]+=c0;
            vecC[row+1]+=c1;
            vecC[row+2]+=c2;
            vecC[row+3]+=c3;
            row+=4;
        }
        //calculate the remaining rows of the result
        for(;row!=n; ++row){
            float* A = matA.storage()+row*k;
            float c0=0;
            for(std::size_t i = 0; i != k; ++i){
                c0+=A[i]*vecB[i];
            }
            vecC[row]+=c0;
        }
    }
    
    //benutzung:
    
    Matrix m(20,10);
    std::vector<float> argument(10);
    std::vector<float> result(20);
    //some initialization skipped here
    prod(m,argument,result);
    

    hat bei mir im Test mit 512er quadratischen Matrizen ATLAS geschlagen. Aber ich gebe zu, dass ein Datenpunkt alleine da nicht wirklich ausschlaggebend ist 😉



  • @Otze:
    Für Matrixprodukt gibt es doch auch ne SSE2 Variante!??

    @cooky451:
    Hi, danke erst einmal das du Dich, auch wenn es etwas Threadfremd ist,
    damit beschäftigts... sieh bitte meine antworten nicht als lernunwillig,
    sondern als debattenbeitrag...

    cooky451 schrieb:

    zeusosc schrieb:

    STL vectoren springen häufig durch verschiedene Unterfunktionen,
    die die Länge prüfen, ob der Zeiger richtig ist und und und,..
    hier wird alloziiert und über maximal zwei ptrs beim zugriff
    dereferenziert (und auf das dritte dan referenziert)...
    sollte also relativ flink laufen,..

    Ein std::vector macht genau das gleiche. (Im Releasemodus). Nein, warte, er ist viel schneller als deine Version, weil er den Index nicht testet.

    echt? is natürlich doof (für mich). Ich werde das nachher mal testen...

    cooky451 schrieb:

    zeusosc schrieb:

    * Zum new: ich vermeide es wo ich kann! (hier nutze ich es exakt 1x)
    Wird der new operator nicht über den source definiert, so findet
    eine (Compiler spezifische) Ersetzung statt, die ich erst zur Laufzeit
    sehe. Das will ich nicht!

    Bitte was?

    Ja, new ist ein operator den man gerne überladen kann. Wird im Fall x
    kein new operator gefunden, z.b. bei einem Basistyp, nimmt der Compiler
    "seine" variante. Das sieht man im trivialsten Fall auch bei
    Zuweisungsoperatoren. Die definition solcher sind im sinne eigentlich
    überladungen....

    cooky451 schrieb:

    zeusosc schrieb:

    * Cast von 0 nach T* : 0 ist kein Typ.
    Sind verschiedene konvertierungsvorschrifften vorhanden,
    wird die erste passende vom Compiler genommen. (Meistens basistypen).
    Ich will das der Compiler "explizit" 0 als T* behandelt.

    Ja.. dann nimm nullptr. 😉

    hmm ja 🙂

    cooky451 schrieb:

    zeusosc schrieb:

    * Cast nach void*: sehe ich jetzt nicht,.. wo?

    In Zeile 58.

    //siehe
    void free ( void * ptr );
    

    cooky451 schrieb:

    zeusosc schrieb:

    * Index operator [] nicht unsigned: Ja,.. kann man so und so sehen,
    "ist übergebene uint > size dann gib referenz" wäre hier wohl
    bessere wahl gewesen, da hast du recht...

    Nein, die bessere Wahl wäre gewesen, den Test einfach nicht zu machen.

    gut,.. wenn es dem aufrufer obligt diesen test zu machen um eine access
    exception zu umgehen...

    cooky451 schrieb:

    zeusosc schrieb:

    * this-> bleibt definitiv !!! es ist ein unterschied ob ich eine
    Methode der Instanz aufrufe oder eine gleichnamige Funktion aus
    dem gleichen Namensraum, geschweige denn Member.
    Da der Compiler das so oder so die addy's der Member aus der
    Referenzierung des this holt (in asm schön sichtbar), dann
    kann ich es auch gleich richtig machen.

    Was heißt hier "richtig", es macht halt keinen Unterschied, aber das this wegzulassen ist übersichtlicher.

    zeusosc schrieb:

    Ausserdem ist dann
    IMMER klar, WANN ich eine Methode oder Member innerhalb einer
    Methode aufrufe,...
    (Macht sich besonders bemerkbar wenn intellisense spinnt)

    Das ist auch so klar. Mehr noch, this-> impliziert, dass diese explizite Schreibweise nötig ist, weil sonst etwas Anderes gemeint wäre. Daher sollte man es meiden, wenn dem nicht so ist.

    Also letzendlich wäre das also nur eine Stilfrage? Du sagst: Nutze wenn muss,
    lasse wenn kann wegen leserlichkeit...

    cooky451 schrieb:

    zeusosc schrieb:

    * Einrückung: hä? wie was? wo?
    Es gibt ja mal wohl mehrere Varianten von einrückungen...

    Das hat nichts mehr mit Stil zu tun, das ist einfach falsch bzw. teilweise gar nicht eingerückt.

    sry, aber pro tiefe ein tab,.. für mich ist das eine entsprechende einrückung..

    cooky451 schrieb:

    zeusosc schrieb:

    * ";" ?? Meines wissens hat nach jeder abgeschlossenen Anweisung ein ";"
    zu folgen. Auch wenn manchen Compilern das wurscht ist....

    Nein, keinem Compiler ist das egal. Aber leere ; sind jedem Compiler egal, deswegen kompiliert der Code überhaupt. Auf einen Scope muss kein ; folgen. (Und sollte dadurch auch nicht.)

    Da kann ich Dir gerade nicht folgen... Hast Du einen entsprechenden Link?

    cooky451 schrieb:

    zeusosc schrieb:

    nicht viele hauerdrinn,...

    Doch, das malloc Gefrickel ist eines der übelsten Sachen die ich seit Langem gesehen habe. Und wenn der Index nicht mehr gültig ist gibt man einfach einen *new T() zurück der nie mehr freigegeben wird? 😮

    Da muss ich Dir wiedersprechen:

    {
    T& foo(void)
    {
       return *(new T());
    };
    
    void blub(void)
    {
        T t;
        t=foo();
    }
    

    Was passiert denn am ende der fkt blub? Det Dtor wird aufgerufen und das Objekt
    zerstört,...
    Das malloc "gefrickel" ist, wie oben erwähnt, die folge das ich kaum ein new benutze,..

    cooky451 schrieb:

    zeusosc schrieb:

    gleich n template mit variabler dim anzahl
    basteln. Er nutzt ja mehrere 1d 2d und 3d arrays.,...

    Das funktioniert nicht. Die Dimensionen sind erst zur Laufzeit bekannt, wären sie zur Compilezeit bekannt, könnte man auch einen std::vector<std::array<...>> nehmen. Daher fällt jegliches Templategezauber weg.

    [/quote]
    Die Dimensionen sind maximal 3-> Schicht, Neuron, gewichte,.. Dynamisch sind
    die Anzahl der Schichten, Neuronen und entsprechend Gewichte,...

    Fazit: ich werde mal n geschwindigkeitsvergleich machen, Vector vs meine Implementation..

    grüße



  • zeusosc schrieb:

    sieh bitte meine antworten nicht als lernunwillig,
    sondern als debattenbeitrag...

    Ich versuche es, auch wenn es mir schwer fällt. 😉

    zeusosc schrieb:

    Ja, new ist ein operator den man gerne überladen kann. Wird im Fall x
    kein new operator gefunden, z.b. bei einem Basistyp, nimmt der Compiler
    "seine" variante. Das sieht man im trivialsten Fall auch bei
    Zuweisungsoperatoren. Die definition solcher sind im sinne eigentlich
    überladungen....

    ??? Ja, operator new kann man überladen. Das ist ja das tolle. Warum sollte das ein Argument sein new nicht zu benutzen? oO

    zeusosc schrieb:

    //siehe
    void free ( void * ptr );
    

    Sag ich ja.
    Sei T* ein Zeiger auf ein Objekt. (Keine Funktion), dann ist
    T* -> void* implizit, benötigt also keinen Cast. (Du nutzt reinterpret_cast.)
    void* -> T* ein Fall für static_cast. (Auch hier verwendest du reinterpret_cast.)
    Man sollte aber immer den "schwächsten" Cast nutzen, für den das Verhalten noch garantiert ist.
    (Weil sich sonst natürlich jeder fragt, warum nutzt der jetzt reinterpret_cast, habe ich was übersehen?)
    (Und weil man sonst den eigentlichen Sinn der C++ Casts unterdrückt, nämlich die Absicht mit der Art des Casts zu verbinden, um so viele Fehler wie möglich zur Compilezeit zu bekommen.)

    zeusosc schrieb:

    gut,.. wenn es dem aufrufer obligt diesen test zu machen um eine access
    exception zu umgehen...

    Du hast grundätzlich zwei Möglichkeiten bei einem Operator, oder einer Funktion die einen Fehler nicht durch den Rückgabewert ausdrückt, so etwas zu behandeln:
    - Ignorieren und es halt drauf ankommen lassen
    - Test und std::out_of_range werfen.
    Siehe std::vector operator[] und .at().
    Aber einfach ein neues Element zurückzugeben ist völliger Quatsch.

    zeusosc schrieb:

    Also letzendlich wäre das also nur eine Stilfrage? Du sagst: Nutze wenn muss,
    lasse wenn kann wegen leserlichkeit...

    Ja, eine Stilfrage. Aber keine besonders Kontroverse. 😉

    zeusosc schrieb:

    sry, aber pro tiefe ein tab,.. für mich ist das eine entsprechende einrückung..

    Wo ist bei den Zeilen 46, 50-67 bitte pro Ebene ein Tab? oO

    zeusosc schrieb:

    Da kann ich Dir gerade nicht folgen... Hast Du einen entsprechenden Link?

    Die ; in den Zeilen 39, 47, 56, 62, 67, 74, 81, 87, 95, 101, 107, 116, 138, 147 und 158 sind sinnlos und damit sehr hässlich.

    zeusosc schrieb:

    Da muss ich Dir wiedersprechen:
    [...]
    Was passiert denn am ende der fkt blub? Det Dtor wird aufgerufen und das Objekt
    zerstört,...

    Argh, du hast das mit der dynamischen Speicherverwaltung (oder dem Destruktor) völlig falsch verstanden. Zähl mal wie viele Objekte erstellt und wie viele zerstört werden: http://ideone.com/5YiqF

    zeusosc schrieb:

    Die Dimensionen sind maximal 3-> Schicht, Neuron, gewichte,.. Dynamisch sind
    die Anzahl der Schichten, Neuronen und entsprechend Gewichte,...

    Die Größe der Dimensionen ist zur Compilezeit nicht bekannt. Vielleicht kannst du ein Template basteln, das immerhin für die Zahl der Dimensionen einen Parameter bereitstellt, der ist ja bekannt. Aber wie willst du dann den Zugriff gestallten? Du bräuchtest also so viele (normale) Argumente, wie Dimensionen. Das wäre also nur mit <cstdarg> möglich - sehr hässlich. Leider. (Oder aber mit variadic Templates. Da könnte was laufen. Aber mein Compiler mag die nicht :(, daher werde ich da jetzt nichts versuchen.)



  • zeusosc schrieb:

    @Otze:
    Für Matrixprodukt gibt es doch auch ne SSE2 Variante!??

    Ja. und eine parallele Variante und eine GPU Variante und eine Clustervariante und... Was ist der Punkt? Das Code, den ich auf ein für einen Anfänger verständliches Niveau runter gebrochen hab, nicht das absolute Performanceoptimum darstellt?

    In der Tat wird jeder momentan übliche Compiler wenn man Optimierungen anschaltet den Code vektorisieren und SSE2-code erzeugen. Natürlich kann man besser als das werden. Viel besser. Aber das ist dann kein Code, den man hier im Forum posten könnte. Das fängt damit an, dass man dafür sorgen muss, dass der Speicher an 16 Byte Grenzen ausgerichtet sein muss und hört dann bei sicherlich 100 Zeilen unverständlichen SSE-Intrinsics auf.

    Natürlich geht es schneller. Und ich würde jedem mit ein wenig Erfahrung empfehlen, sich mal eine explizite Vektorisierung anzutun. Aber SSE macht aus schlechtem Code keine Rakete. Und wie man hier im Thread - und auch bei dir - sieht, würden die Flaschenhälse bei den meisten Leuten einfach ganz woanders liegen. Das fängt schon mal damit an, dass du zum Beispiel die Laufzeit von tanh völlig überschätzt. 1 tanh kannst du auf double Präzision in weniger als 150 Takten berechnen. Das sind ganz schön viele tanh pro Sekunde. Und mal so nebenbei: wenn es eine so einfache Möglichkeit gäbe, tanh bei ähnlicher Präzision erheblich schneller zu berechnen, dann würden die Hersteller der Laufzeitbibliotheken diese mitliefern. Ähnliches gilt für std::vector (Bemerkenswerte Ausnahme ist std::complex, das kann nach den Vorgaben des Standards gar nicht schnell sein).

    Wie du siehst, kann man da gar nicht so viel falsch machen, außer std::vector falsch zu verwenden, natürlich. Aber bei der linearen Algebra. Da kannst du richtig viel verlieren.
    Es ist mal wieder Zeit für meinen liebsten Benchmark.
    http://eigen.tuxfamily.org/index.php?title=Benchmark

    Da kommt der Faktor 10 her.


Anmelden zum Antworten