Frage zu xmm instructions unter VC++2010



  • Hallo! Ich habe mich heute mal an die xmm instrinsics von Visual Studio 2010 gewagt. Mein Code orientiert sich an einem online tutorial, das ich gefunden habe. Dann habe ich ein bisschen selber rumgespielt, und bin über Folgendes gestolpert:

    #include <xmmintrin.h>
    const int N = 1000;
    void foo()
    {
    	__declspec(align(16)) float arr[N] = {};
    
    	__m128 *B = (__m128*)arr;
    	for(unsigned i=0; i<N-5; i+=4)
    	{
    		__m128 *p1 = (__m128*)(arr);
    		__m128 *p2 = (__m128*)(arr+1);
    
    		__m128 tmp1 = _mm_add_ps(p1[i], p2[i]);
    		__m128 tmp2 = _mm_set1_ps(2.f);
    
    		*B = _mm_add_ps(*B, /* tmp1 oder tmp2 */);
    	}
    }
    int main(){foo();}
    

    Vorab: Mir ist bewusst, dass die Funktion nichts sinnvolles anstellt. Es soll nur ein Minimalbeispiel sein.
    Zu meinem Problem: Wenn ich in der letzten Zeile als 2. Argument an das _mm_add_ps das "tmp2" übergebe, läuft das Programm normal. Wenn ich aber "tmp1" übergebe, stürzt es mit einer Access Violation ab. Das verstehe ich nicht. Sollte es für den Compiler nicht egal sein, welches Objekt er hier in das Array "rein-addiert"?



  • XMM schrieb:

    Wenn ich in der letzten Zeile als 2. Argument an das _mm_add_ps das "tmp2" übergebe, läuft das Programm normal.

    Was nichts zu bedeuten hat.

    __m128 tmp1 = _mm_add_ps(p1[i], p2[i]);
    

    mit i=992 wird dich nicht glücklich machen.



  • Hm, ich fürchte, ich habe hier etwas ganz Grundlegendes nicht verstanden.
    Meinst du damit, dass ich über die Array-Granze hinaus lesen? Bei i=992 sollte doch p2 maximal auf arr[992+4+1] zugreifen, was noch im Array drin liegt.
    Ich habe gerade mal geschaut, bei welchem Schleifendruchlauf das Programm abstürzt: Es stürzt schon im 1. Durchlauf ab (also i=0).



  • XMM schrieb:

    ...
    		__m128 *p2 = (__m128*)(arr+1);
    

    XMM-Variablen müssen an einer 16-Byte-Grenze ausgerichtet sein. Da arr an einer solchen ausgerichtet ist ( __declspec(align(16)) ) , ist arr+1 nur 4 Bytes (Float) weiter - also nicht mehr ausgerichtet. Du musst schon 4 Floats weiterschalten, um wieder an einer 16-Byte-Grenze zu landen:

    __m128 *p2 = (__m128*)(arr+4);
    

    viele grüße
    ralph



  • Ah, das macht Sinn! Danke!



  • Macht Visual Studio daraus nicht automatisch einen unaligned read?



  • knivil schrieb:

    Macht Visual Studio daraus nicht automatisch einen unaligned read?

    So sieht das assemblermäßig aus:

    ;         __m128 tmp1 = _mm_add_ps(p1[i], p2[i]);
    
    	mov	edx, DWORD PTR _i$4326[ebp]
    	shl	edx, 4
    	add	edx, DWORD PTR _p2$4332[ebp]
    	movaps	xmm0, XMMWORD PTR [edx]
    	mov	eax, DWORD PTR _i$4326[ebp]
    	shl	eax, 4
    	add	eax, DWORD PTR _p1$4330[ebp]
    	movaps	xmm1, XMMWORD PTR [eax]
    	addps	xmm1, xmm0
    	movaps	XMMWORD PTR $T4335[ebp], xmm1
    	movaps	xmm0, XMMWORD PTR $T4335[ebp]
    	movaps	XMMWORD PTR _tmp1$4334[ebp], xmm0
    

    p1[i] und p2[i] werden mit movaps geladen - also aligned. p2[i] kann allerdings nicht aligned sein, wenn p2 nur 4 Bytes hinter p1 beginnt. Es ist bedauerlich, dass VC zwar darauf achtet, dass __m128-Variablen an 16-Bytes-Grenzen ausgerichtet werden (http://msdn.microsoft.com/en-us/library/ayeb3ayc.aspx), aber nicht merkt, wenn (gecastete) __m128-Zeiger nicht restlos durch 16 teilbar sind.

    viele grüße
    ralph


Anmelden zum Antworten