Und noch ne Anfängerfrage


  • Mod

    soerenP schrieb:

    mov ecx,this
    	movaps xmm3,[ecx]PMFdmOsString.m_a1
    

    ...erzeugt folgenden Fehler:
    error C2411: 'm_a1' : illegal struct/union member in 'second operand'

    Bei mir nicht. Zeig mal die Funktion, in der der Fehler auftritt.

    soerenP schrieb:

    Wenn ich einen 64Bit-Prozessor habe, kann ich mir dadurch keine Probleme einfangen, oder? Will nur alles ausschließen...

    Visual C++ unterstützt sowieso keinen inline-Assembler für 64bit-Targets.

    soerenP schrieb:

    Was mich stutzig macht ist, warum er auf 0xffffffff zugreifen möchte:
    this = 0x0b8da008
    m_a1 = 0x0b8da298

    Offensichtlich ist das Objekt nicht auf 16Byte ausgerichtet, damit muss ein movaps fehlschlagen. Zwar hast du diese mit __declspec deklariert, und diese Deklaration wirkt sich auch auf das Alignment des gesamten Objektes aus. Es ändert aber nicht das Verhalten von Allokationsfunktionen - die Deklaration mir align(16) genügt daher nur für statische und automatische Variablen. Die Allokationsfunktionen der Standardbibliothek ebenso wie die C-Funktionen malloc,realloc etc. liefern Speicher, der hinreichend ausgerichtet für jeden Standardtypen ist. Die strengste Ausrichtung, die ein solcher Typ hat, ist aber nur 8 - das genügt nicht für sse. Du hast zwei Möglichkeiten: entweder verzichtest du auf Befehle, die ausgerichtete Operanden verlangen (movaps -> movups), das ist aber nicht unbedingt empfehlenswert. Oder du schreibst eine eigene Allokationsfunktion, global oder Klassenspezifisch. Die Überladung in der Klasse hat den Vorteil geringeren Speicherverbrauchs, da sie nur mit den Klassen arbeitet, die dies wirklich benötigen - andererseits ist eine globale Lösung einfacher zu benutzen (du brauchst die Funktion nicht nur für PMFdmOsString, sondern auch für jede andere Klasse, die PMFdmOsString als nicht-statischen Member hat). Das kann z.B. so durch Zurückführung auf die Standardversionen geschehen (wir reservieren ein bisschen mehr Speicher, und suchen uns die nächste hinreichend ausgerichtete Adresse dieses Speicherblocks, unmittelbar vor dieser Adresse speichere ich einen Zeiger auf den Anfang des Speicherblocks, den ich dann zum Löschen benötige):

    class PMFdmOsString:public PMFdmString
    {
    public:
        static void* operator new(std::size_t size)
        {
            static const std::size_t alignment = 16;
            static const std::size_t offset = alignment + sizeof( char* );
            char* p = new char[ size + offset ]; // das geht elegant bei klassenspezifischen Funktionen
            // bei globaler Ersetzung könnten wir nicht einfach die Version der Standardbibliothek aufrufen,
            // in dieser Hinsicht ist die Überladung in der Klasse eleganter
            std::size_t pp = ( reinterpret_cast< std::size_t >( p ) + offset ) & ~( alignment - 1 );
            char** q = reinterpret_cast< char** >( pp ); // das ist offensichtlich nicht portabel, aber eine portable Lösung wird prinzipiell erst mit C++0x möglich sein
            q[ -1 ] = p;
            return q;
        }
        static void operator delete(void* p)
        {
            delete [] static_cast< char** >( p )[ -1 ];
        }
        static void* operator new[](std::size_t size)
        {
            return operator new( size );
        }
        static void operator delete[](void* p)
        {
            operator delete( p );
        }
    // usw.
    
    };
    

    Effizienter wäre nat. die Implementation eines eigenen Allokators, der mit einem eigenen bereits ausgerichteten Speicherpool arbeitet - dann hat man nicht diese zusätzlichen Kosten für jedes einzelne Objekt.
    Nebenbei gesagt, sollte der Destruktor von PMString virtuell sein.


  • Mod

    Ich hab noch ein bisschen gebastelt

    #include <new>
    #include <boost/type_traits/alignment_of.hpp>
    
    namespace SSE
    {
    
    struct EmptyBase {};
    
    struct StrangeClass : virtual EmptyBase { virtual ~StrangeClass(); };
    struct Alignments
    {
        long long a;
        void* b;
        StrangeClass* c;
        StrangeClass d;
        long double e;
        void(StrangeClass::*f)();
        char StrangeClass::*g;
    };
    const std::size_t max_standard_alignment = boost::alignment_of< Alignments >::value;
    
    template<std::size_t alignment, bool = ( alignment > max_standard_alignment ) >
    struct aligned_new_delete
    {
        static void* malloc(std::size_t size)
        {
            char* unaligned_storage = new( std::nothrow ) char[ size + alignment ];
            if ( !unaligned_storage )
                return NULL;
            std::size_t unaligned_address = reinterpret_cast< std::size_t >( unaligned_storage );
            std::size_t aligned_address = ( unaligned_address + alignment ) & ~( alignment - 1 );
            char** aligned_storage = reinterpret_cast< char** >( aligned_address );
            new( aligned_storage - 1 ) char*( unaligned_storage );
            return aligned_storage;
        }
        static void free(void* aligned_block)
        {
            char** aligned_storage = static_cast< char** >( aligned_block );
            delete [] aligned_storage[ -1 ];
        }
    };
    
    template<std::size_t alignment>
    struct aligned_new_delete< alignment, false >
    {
        static void* malloc(std::size_t size) { return new( std::nothrow ) char[ size ]; }
        static void free(void* p) { delete [] static_cast< char* >( p ); }
    };
    
    template<typename Derived, typename Base = EmptyBase>
    struct AlignedAlloc : public Base
    {
        AlignedAlloc()
            : Base()
        {}
        template<typename T0>
        AlignedAlloc(const T0& arg0)
            : Base( arg0 )
        {}
        template<typename T0, typename T1>
        AlignedAlloc(const T0& arg0, const T1& arg1)
            : Base( arg0, arg1 )
        {}
        template<typename T0, typename T1, typename T2>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2)
            : Base( arg0, arg1, arg2 )
        {}
        template<typename T0, typename T1, typename T2, typename T3>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3)
            : Base( arg0, arg1, arg2, arg3 )
        {}
        template<typename T0, typename T1, typename T2, typename T3, typename T4>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4)
            : Base( arg0, arg1, arg2, arg3, arg4 )
        {}
        template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5)
            : Base( arg0, arg1, arg2, arg3, arg4, arg5 )
        {}
        template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6)
            : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6 )
        {}
        template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6, const T7& arg)
            : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 )
        {}
        template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6, const T7& arg7, const T8& arg8)
            : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 )
        {}
        template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
        AlignedAlloc(const T0& arg0, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5, const T6& arg6, const T7& arg7, const T8& arg8, const T9& arg9)
            : Base( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 )
        {}
    
        static void* operator new(std::size_t size, const std::nothrow_t&)      { return aligned_new_delete< boost::alignment_of< Derived >::value >::malloc( size ); }
        static void* operator new[](std::size_t size, const std::nothrow_t& t)  { return operator new( size, t ); }
        static void operator delete(void* p, const std::nothrow_t&)             { aligned_new_delete< boost::alignment_of< Derived >::value >::free( p ); }
        static void operator delete[](void* p, const std::nothrow_t& t)         { operator delete( p, t ); }
    
        static void* operator new(std::size_t size)                             { void* p = operator new( size, std::nothrow ); return p ? p : throw std::bad_alloc(); }
        static void* operator new[](std::size_t size)                           { return operator new( size ); }
        static void operator delete(void* p)                                    { operator delete( p, std::nothrow ); }
        static void operator delete[](void* p)                                  { operator delete( p ); }
    
        static void* operator new(std::size_t size, void* p)                    { return ::operator new( size, p ); }
        static void* operator new[](std::size_t size, void* p)                  { return ::operator new[]( size, p ); }
        static void operator delete(void* p, void* q)                           { ::operator delete( p, q ); }
        static void operator delete[](void* p, void* q)                         { ::operator delete[]( p, q ); }
    };
    
    }
    

    AlignedAlloc ist als Mixin konzipiert, dass die notwendigen Allokationsfunktion automatisch generiert:

    struct Foo : SSE::AlignedAlloc< Foo >
    {
    };
    
    struct Bar : SSE::AlignedAlloc< Bar, Foo >
    {
        __declspec(align(16)) float x[4];
    };
    

    Der erste Parameter ist die Klasse, für die wir die Funktion brauchen, die also von dem Mixin ableitet. Der zweite optionale Parameter gibt eine Klasse an, von der das Mixin ableitet. Damit kann das Mixin in eine bestehende Klassenhierarchie eingebunden werden, ohne dass man deswegen gleich Mehrfachvererbung nutzen muss (man kann das natürlich tun) - das kann effizienter sein. Als Mixin ist AlignedAlloc natürlich nicht als polymorphe Basisklasse gedacht. Das Ganze ist so konzipiert, dass kein Overhead entsteht, wenn man das Mixin auf Klassen anwendet, die eigentlich keine eigene Allokationsfunktion benötigen, man kann es also sehr liberal nutzen.

    Zum Beispiel würden sich die notwendigen Änderungen im ursprünglichen Code dann darauf beschränken, die Zeile

    class PMFdmString : public PMString
    

    durch

    class PMFdmString : public SSE::AlignedAlloc< PMFdmString, PMString >
    

    zu ersetzen.



  • Also, ich hab zwar nicht wirklich verstanden, was genau in der new() funktion passiert, ich nehme aber an, sie liefert mir richtig ausgerichteten Speicher?

    Wie sieht es eigentlich mit addps aus? In meiner Assembler Referenz steht nichts davon, dass, wenn der zweite Operand aus dem Speicher stammt, dieser auch aligned sein muss. Scheint aber so zu sein...


  • Mod

    soerenP schrieb:

    Also, ich hab zwar nicht wirklich verstanden, was genau in der new() funktion passiert, ich nehme aber an, sie liefert mir richtig ausgerichteten Speicher?

    genau. so kannst du new/new[] ganz normal einsetzen.

    soerenP schrieb:

    Wie sieht es eigentlich mit addps aus? In meiner Assembler Referenz steht nichts davon, dass, wenn der zweite Operand aus dem Speicher stammt, dieser auch aligned sein muss. Scheint aber so zu sein...

    Speicheroperanden müssen grundsätzlich ausgerichtet sein, es sei denn, der Befehl spezifiziert ausdrücklich etwas anderes, das ist an sich nur bei den entsprechenden movxyz-Befehlen der Fall.



  • Ich korrigiere mich (Das letzte Posting bezog sich auf das zwei vorher): Ich versteh nur noch Bahnhof. So etwas sag ich nicht gerne, aber es ist gerade angebracht. Ich glaube, die obige Version gefällt mir doch ganz gut...;)

    Kannst du mir jetzt noch sagen, warum ich beim Rücksprung in eine übergeordnete (aufrufende) Funktion eine Acces-Violation bekomme. Im Call-Stack (ganz oben) steht nur 0a74fcfc()...
    Ist irgendwas mit irgendwelchen Rücksprung-Adressen falsch?
    Das kann doch eigentlich alles gar nicht so schwierig sein....

    Ein riesen Dank für deine Bemühungen!
    Sören


  • Mod

    naja, da ist auch jede Menge syntaktischer Lärm dabei, um für alle möglichen Eventualitäten vorzusorgen. Der wirklich interessante Teil ist der Gleiche wie zuvor.

    Kannst du mir jetzt noch sagen, warum ich beim Rücksprung in eine übergeordnete (aufrufende) Funktion eine Acces-Violation bekomme. Im Call-Stack (ganz oben) steht nur 0a74fcfc()...
    Ist irgendwas mit irgendwelchen Rücksprung-Adressen falsch?

    Code musst du schon zeigen.



  • Also, hier erstmal mein Assembler Code:

    mov ecx,this
    				movups xmm3,PMFdmOsString::m_a1[ecx]//Move coefficients to registers
    				movups xmm4,PMFdmOsString::m_a2[ecx]
    				movups xmm5,PMFdmOsString::m_a3[ecx]
    				movups xmm6,PMFdmOsString::m_a4[ecx]
    				movups xmm7,PMFdmOsString::m_a5[ecx]
    				mov eax,8		//Prepare Loop
    				mov ebx,16		//we are doing 4 steps at a time
    				mov edx,end		//eax is start and ecx is end of the loop
    
    schleife:
    				movups xmm1,[pm_y_t+eax-8]//t[x-2]*a4
    				movups xmm2,[pm_y_t+eax+8]//t[x+2]*a4
    				addps xmm1,xmm2
    				mulps xmm1,xmm6//after having added, multiply with coefficient a4
    				movaps xmm0,xmm1//move to other register, where the multiplication results can be accumulated
    
    				movups xmm1,[pm_y_t+eax-4]//t[x-1]*a3
    				movups xmm2,[pm_y_t+eax+4]//[t][x+1]*a3
    				addps xmm1,xmm2
    				mulps xmm1,xmm5//multiply with coefficient
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movups xmm1,[pm_y_t+eax]//[t][x]*a1
    				mulps xmm1,xmm3
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movups xmm1,[pm_y_tm1+eax]//[t-2][x]*a2
    				mulps xmm1,xmm4
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movups xmm1,[pm_y_tm2+eax]//[t-2][x]*a5
    				movups xmm2,[pm_y_tm1+eax-4]
    				addps xmm1,xmm2
    				movups xmm2,[pm_y_tm1+eax+4]
    				addps xmm1,xmm2
    				mulps xmm1,xmm7
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movntps [pm_y_tp1+eax],xmm0 //write accumulation-result to memory
    				add eax,ebx //increase loop counter by 16Bytes
    				cmp eax,edx
    				jb schleife
    				ret
    

    Dort, wo die Exception auftritt sieht die Dissemblierung so aus:

    0A74FCFA  je          0A74FD06 
    //Hier tritt der Fehler auf:
    0A74FCFC  les         edx,fword ptr [ebx-6Bh] 
    0A74FCFF  or          eax,dword ptr [eax] 
    0A74FD01  add         byte ptr [eax],al
    

    und zwar ist ebx=0, was wahrscheinlich nicht so sein sollte.
    Die Register zu sichern mit pusha, popa hat auch nichts gebracht.
    Hat dir das was gebracht?
    Ich weiß nicht, was sonst noch interessant sein könnte
    Grüße
    Sören


  • Mod

    zeig immer den ganzen Funktionsrumpf. Ich würde aber darauf tippen, dass das ret dort nicht hingehört. Außer in naked-Funktionen solltest du Funktionen niemals direkt mit ret verlassen (und naked ist hier nicht angebracht).


  • Mod

    nebenbei gesagt, gibt es eigentlich keinen Grund, die Koeffizienten m_a als sse-Vektor zu speichern, wenn sie sowieso in allen Komponenten gleich sind. Es dürfte sinnvoller sein, sie normal als float zu speichern und dann erst bei der Verarbeitung zu duplizieren:

    movss xmm3,PMFdmOsString::m_a3[ecx]
    shufps xmm3,xmm3,0
    


  • Oha!
    Ja, gut zu wissen, alles. Vielen Dank!
    Was die koeffizienten angeht: Ich dachte, man könnte mit der Aktion vielleicht einen Schritt sparen. Mal schauen...
    Sören



  • Und das nächste Problem:
    Diesmal ist der this-Pointer nach der Aktion im Arsch. Der steht auf irgendeinem quatsch-Wert. Er sollte ungefähr auf:0x0b950063 stehen, steht aber auf 0xc79a39ab.
    Gibt es irgendeine Stelle, wo der this Pointer von irgendwoher geladen wird und an dort nicht das richtige steht? ecx? In ecx steht übrigens noch die richtige Adresse, wie kann da der this Pointer kaputt gehen?
    Grüße
    Sören


  • Mod

    soerenP schrieb:

    Und das nächste Problem:
    Diesmal ist der this-Pointer nach der Aktion im *****. Der steht auf irgendeinem quatsch-Wert. Er sollte ungefähr auf:0x0b950063 stehen, steht aber auf 0xc79a39ab.
    Gibt es irgendeine Stelle, wo der this Pointer von irgendwoher geladen wird und an dort nicht das richtige steht? ecx? In ecx steht übrigens noch die richtige Adresse, wie kann da der this Pointer kaputt gehen?
    Grüße
    Sören

    höchstwahrscheinlich ist this irgendwo auf dem stack zu finden, jedenfalls im Assemblerteil. Aber ohne Code kann ich nur raten.



  • Den Code kennst du ja;)...
    Direkt nach dem Rücksprung zu C++ ist der Pointer nicht mehr der alte und es gibt bei einem simplen t++ (member-Variable) natürlich eine Access-Violation, weil er logischerweise t dann auch nicht mehr kennt, wenn der this pointer falsch ist...

    //Hier steht der Code vom früheren Posting
                           jb schleife
    			}//asm	
    			t++;//hier tritt die Acces violation auf
    

    Bis denn und schönen Abend
    Sören


  • Mod

    söän schrieb:

    Den Code kennst du ja;)...

    Eben nicht. Ich kenne den Rest der Funktion nicht. Um ehrlich zu sein, ich bin mir einigermaßen sicher, wo der Fehler liegt - Gewissheit erlange ich aber eben erst durch den Rest des Codes. Ich schreibe nur ungern über Dinge, die ich nur vermuten kann.
    vor allem interessiert mich, wie pm_y_t, pm_y_tm1, pm_y_tm2, pm_y_tp1, end definiert wurden und welche Werte sie haben.



  • Na guuuut...
    Hier haste ihn:

    float PMFdmOsString::getOutput()
    {
    	if(m_disable)return 0;
    	int end=4*(m_nElements-2);//!!!Hier ist END!!!
    	m_forceBuffer[m_forceBufferPointer][2]=m_bodyInputBuffer*0.0005*m_bodyInteractionFactor;
    	if(!tester.m_sse)
    	{
    		for(int i=0;i<m_osFactor;i++)
    		{
    			float *pm_y_tp1= m_y[tp1],
    		  *pm_y_tm1= m_y[tm1],
    		  *pm_y_tm2= m_y[tm2],
    		  *pm_y_t= m_y[t],
    		  *pm_forceBuffer_m_forceBufferPointer=
    			  m_forceBuffer[m_forceBufferPointer];
    
    			for(int x=2;x<m_nElements-2;x++)
    			{
    				pm_y_tp1[x]=
    					m_coeff_a1*pm_y_t[x]
    					+m_coeff_a2*pm_y_tm1[x]
    					+m_coeff_a3*(pm_y_t[x+1]+pm_y_t[x-1])
    					+m_coeff_a4*(pm_y_t[x+2]+pm_y_t[x-2])
    					+m_coeff_a5*(pm_y_tm1[x+1]+pm_y_tm1[x-1]+pm_y_tm2[x])
    				+pm_forceBuffer_m_forceBufferPointer[x]; 
    			}
    			/*
    			for(int x=2;x<m_nElements-2;x++)
    			{
    			m_y[tp1][x]=
    				m_coeff_a1*m_y[t][x]
    				+m_coeff_a2*m_y[tm1][x]
    				+m_coeff_a3*(m_y[t][x+1]+m_y[t][x-1])
    				+m_coeff_a4*(m_y[t][x+2]+m_y[t][x-2])
    				+m_coeff_a5*(m_y[tm1][x+1]+m_y[tm1][x-1]+m_y[tm2][x])
    			+m_forceBuffer[m_forceBufferPointer][x];
    			}*/
    			t++;
    			tp1++;
    			tm1++;
    			tm2++;
    			if(t==4)t=0;
    			if(tp1==4)tp1=0;
    			if(tm1==4)tm1=0;
    			if(tm2==4)tm2=0;
    			if(m_forceBufferPointer>0)m_forceBufferPointer--;
    			if(i<m_osFactor-1)m_aaFilter->pushSample(m_y[t][2]);
    		}
    	}
    
    	else
    	{
    		for(int i=0;i<m_osFactor;i++)
    		{
    //Hier sind die Pointer deklariert und initialisiert
    			float *pm_y_tp1= m_y[tp1],
    		  *pm_y_tm1= m_y[tm1],
    		  *pm_y_tm2= m_y[tm2],
    		  *pm_y_t= m_y[t],
    		  *pm_forceBuffer_m_forceBufferPointer=
    			  m_forceBuffer[m_forceBufferPointer];
    			/*
    			for(int x=2;x<m_nElements-2;x++)
    			{
    				pm_y_tp1[x]=
    					m_coeff_a1*pm_y_t[x]
    					+m_coeff_a2*pm_y_tm1[x]
    					+m_coeff_a3*(pm_y_t[x+1]+pm_y_t[x-1])
    					+m_coeff_a4*(pm_y_t[x+2]+pm_y_t[x-2])
    					+m_coeff_a5*(pm_y_tm1[x+1]+pm_y_tm1[x-1]+pm_y_tm2[x])
    				+pm_forceBuffer_m_forceBufferPointer[x]; 
    			}*/
    
    			__asm
    			{
    				mov ecx,this
    				movups xmm3,PMFdmOsString::m_a1[ecx]//Move coefficients to registers
    				movups xmm4,PMFdmOsString::m_a2[ecx]
    				movups xmm5,PMFdmOsString::m_a3[ecx]
    				movups xmm6,PMFdmOsString::m_a4[ecx]
    				movups xmm7,PMFdmOsString::m_a5[ecx]
    				mov eax,8		//Prepare Loop
    				mov ebx,16		//we are doing 4 steps at a time
    				mov edx,end		//eax is start and ecx is end of the loop
    
    schleife:
    				movups xmm1,[pm_y_t+eax-8]//t[x-2]*a4
    				movups xmm2,[pm_y_t+eax+8]//t[x+2]*a4
    				addps xmm1,xmm2
    				mulps xmm1,xmm6//after having added, multiply with coefficient a4
    				movaps xmm0,xmm1//move to other register, where the multiplication results can be accumulated
    
    				movups xmm1,[pm_y_t+eax-4]//t[x-1]*a3
    				movups xmm2,[pm_y_t+eax+4]//[t][x+1]*a3
    				addps xmm1,xmm2
    				mulps xmm1,xmm5//multiply with coefficient
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movups xmm1,[pm_y_t+eax]//[t][x]*a1
    				mulps xmm1,xmm3
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movups xmm1,[pm_y_tm1+eax]//[t-2][x]*a2
    				mulps xmm1,xmm4
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movups xmm1,[pm_y_tm2+eax]//[t-2][x]*a5
    				movups xmm2,[pm_y_tm1+eax-4]
    				addps xmm1,xmm2
    				movups xmm2,[pm_y_tm1+eax+4]
    				addps xmm1,xmm2
    				mulps xmm1,xmm7
    				addps xmm0,xmm1//accumulate in xmm0
    
    				movntps [pm_y_tp1+eax],xmm0 //write accumulation-result to memory
    				add eax,ebx //increase loop counter by four
    				cmp eax,edx
    				jb schleife
    			}//asm
    
    			t++;
    			tp1++;
    			tm1++;
    			tm2++;
    			if(t==4)t=0;
    			if(tp1==4)tp1=0;
    			if(tm1==4)tm1=0;
    			if(tm2==4)tm2=0;
    			if(m_forceBufferPointer>0)m_forceBufferPointer--;
    			if(i<m_osFactor-1)m_aaFilter->pushSample(m_y[t][2]);
    		}
    	}//using sse
    	//To prevent the denormalization bug: avoid too small values 
    	if(m_y[t][m_exciterElement]<0.0000000119f&&m_y[t][m_exciterElement]>-0.0000000119f)m_denormalGuardCounter++;
    	else m_denormalGuardCounter=0;
    	if(m_denormalGuardCounter>=40)
    	{
    		for(int i=0;i<4;i++)
    		memset(m_y[i],0,m_nElements*sizeof(float));
    		m_disable=true;
    		m_denormalGuardCounter=0;
    	}
        float output=m_aaFilter->getOutput(m_y[t][2]);
    	(*m_bodyOutput)+=output;
    	return output;
    }
    


  • hat zwar nichts mit deiner frage zu tun, aber wo ich das hier gerade sehe...

    if(m_denormalGuardCounter>=40)
    

    ...nehme ich an, dass dich evtl. das interessiert.



  • Nicht, dass es ein Problem wäre, aber habe ich das richtig verstanden, dass das nur unter SSE2 geht?
    Dass DAZ und FZ Flag gibts nämlich erst da.


  • Mod

    Das Problem sind alle Adressberechnungen:

    [pm_y_t+eax-8]
    [pm_y_tp1+eax]
    

    usw.

    Ein einzelner Befehl für immer nur maximal einen Lese oder Schreibvorgang auf dem Speicher aus (Lesen und Schreiben zugleich nur, wenn ein Speicheroperand gleichzeitig Quelle und Ziel ist).

    Das bedeutet, dass wenn eine Variable in einer komplexen Adressierung verwendet wird, zunächst nur die Adresse dieser Variablen für die Berechnung der Adresse des Speicheroperanden genutzt wird, und dann erst vom Ergebnis dieser Adressberechnung gelesen wird.
    [pm_y_t+eax-8] ist also sinngemäß *(&pm_y_t+(eax-8)/sizeof(float)) - dein movntps zerstört also die Stackstruktur oberhalb von pm_y_t.

    Was du möchtest, ist erst den Zeiger zu lesen, und dann mit dem Ergebnis dieses Lesevorgangs eine Adresse zu berechnen, von der anschließend gelesen wird.
    Tatsächlich - das muss bei komplexer Adressierung berücksichtigt werden - ist pm_y_t in als Adressausdruck keine Konstante, da es sich um eine automatische Variable handelt, sondern ein Ausdruck relativ zu ebp.

    Da du ja noch ein paar freie Register hast (ecx,esi,edi) kannst du diese Zeiger ja vor der Schleife einfach einmal laden.



  • Aha, fast schon amüsant, was man als Anfänger alles so verbocken kann...
    Und noch einmal:
    Besten Dank!
    Sören



  • Na also, da hammas!
    Nachdem ich den MOVNTPS noch in ein MOVUPS verwandeln musste. Der MOVNTPS ist bestimmt besser, verlangt aber natürlich auch aligned-Speicher. Und selbst, wenn mein Speicher aligned ist, hab ich immer einen Offset von 8 Bytes weil ich bei y[tp1][2] anfange zu zählen. Kann man auch Speicher machen, der genau NICHT auf 16 aligned ist, aber auf 8?
    Viele Grüße
    Sören


Anmelden zum Antworten