تبدیل وکتور به __m128i و جمع زدن 2 وکتور با SSE - هفت خط کد انجمن پرسش و پاسخ برنامه نویسی

وبـــلاگ هــفت خــط کــد


آموزش های برنامه نویسی
۴۳۶ نفر آنلاین
۲۰۳ عضو و ۲۳۳ مهمان در سایت حاضرند

تبدیل وکتور به __m128i و جمع زدن 2 وکتور با SSE

+2 امتیاز

من متوجه نمیشم به چه شکل میشه یک وکتور ازint رو به تابع زیر که برای جمع با SSE هست پاس داد

extern __m128i _mm_add_epi32(__m128i _A, __m128i _B);

من تعریف __m128i رو که نگاه کردم این هست :

typedef union __declspec(intrin_type) _CRT_ALIGN(16) __m128i {
    __int8              m128i_i8[16];
    __int16             m128i_i16[8];
    __int32             m128i_i32[4];
    __int64             m128i_i64[2];
    unsigned __int8     m128i_u8[16];
    unsigned __int16    m128i_u16[8];
    unsigned __int32    m128i_u32[4];
    unsigned __int64    m128i_u64[2];
} __m128i;

الان اگر من 2 تا وکتور داشته باشم چطوری میتونم با استفاده از تابع بالا جمعشون بزنم ؟؟

std::vector<int> a;
std::vector<int> b;
std::vector<int> res= //jam zadan a va b ba sse

 

سوال شده شهریور 4, 1393  بوسیله ی OptiMan (امتیاز 124)   2 9 16

1 پاسخ

+4 امتیاز
 
بهترین پاسخ

SSE  از اونجایی که عملیات هایی که انجام میده بر خلاف حالت عادی روی register های 128 بیتی انجام میشه(رجیستر های XMM) شما اول باید 128 بیت از 2 وکتور جدا کنید و داخل union ای که ذکر کردید قرار بدید (برای هر وکتور یک آنیون )بعد  این 2 تا آنیون رو با توابعی که هست یا مستقیما با اسمبلی جمع بزنید و در نهایت آنیونی که نتیجه جمع 2 وکتور هست رو به وکتور سوم انتقال بدیم .

 

برای انجام چیزایی که گفتم بترتیب این کارها باید انجام بشن :

 

اول دیتا  هر 2 تا وکتور ورودی  که از نوع int* هست رو به __m128i* تبدیل می کنید :

std::vector<int> a,b;
__m128i* src= reinterpret_cast<__m128i*>(a.data());
__m128i* src2= reinterpret_cast<__m128i*>(b.data());

 

حالا برای load کردن دیتا  از وکتور به آنیون __mm128i از یکی از  این 3 تابع استفاده می کنیم : 

//khoondan 128 bit  az p
//hatman bayad p 16bit align bashe
extern __m128i _mm_load_si128(__m128i const*_P);

//khoondan 128bit az p
//niayze be align boodan nist
extern __m128i _mm_loadu_si128(__m128i const*_P);

//64bit az p mikhoone va 64bit aval register mmx ro set mikone 64bit badi ro 0 mizare
extern __m128i _mm_loadl_epi64(__m128i const*_P);//l mokhafaf low hast(low quad word)

مثال : 

std::vector<int> a={1,2,3,4};
__m128i* src= reinterpret_cast<__m128i*>(a.data());
__m128i var=_mm_load_si128(src);//var alan shamele 1,2,3,4 hast

کار بالا رو برای src2 هم انجام میدیم 

برای جمع زدن 2 تا __mm128i از یکی از این توابع استفاده می کنیم :

extern __m128i _mm_add_epi8(__m128i _A, __m128i _B);
extern __m128i _mm_add_epi16(__m128i _A, __m128i _B);
extern __m128i _mm_add_epi32(__m128i _A, __m128i _B);
extern __m64 _mm_add_si64(__m64 _A, __m64 _B);
extern __m128i _mm_add_epi64(__m128i _A, __m128i _B);
extern __m128i _mm_adds_epi8(__m128i _A, __m128i _B);
extern __m128i _mm_adds_epi16(__m128i _A, __m128i _B);
extern __m128i _mm_adds_epu8(__m128i _A, __m128i _B);
extern __m128i _mm_adds_epu16(__m128i _A, __m128i _B);

 u مخفف unaligned هست 

عددی هم که بعد از epi هست نشون میده عملیات روی داده چند بیتی انجام میشه مثلا  epi8 یعنی جمع روی char که 8 بیت هست انجام میشه یا epi32 روی int که 32 بیته 

مثال از استفاده :

std::vector<int> a={1,2,3,4};
std::vector<int> b={1,2,3,4};
__m128i* src = reinterpret_cast<__m128i*>(a.data());
__m128i* src2= reinterpret_cast<__m128i*>(b.data());

__m128i lhs=_mm_load_si128(src);
__m128i rhs=_mm_load_si128(src2);

//chon vector az no e int 32biti hast az epi32 estefade mikonim
__m128i  result=_mm_add_epi32(lhs,rhs);//result inja shamele majmoo hast

برای ذخیره _mm128i داخل وکتور هم از یکی از این توابع :

//128 bit az _B dakhel _p zakhire mishe p bayad 16bit align shode bashe
extern void _mm_store_si128(__m128i *_P, __m128i _B);

//128 bit az _B dakhel _p zakhire mishe 
extern void _mm_storeu_si128(__m128i *_P, __m128i _B);

//64 bit aval  az Q dakhe p zakhire mishe
extern void _mm_storel_epi64(__m128i *_P, __m128i _Q);

 

پس با استفاده از توابع بالا میشه تابع جمع 2 وکتور رو به این شکل نوشت : 

#include <iostream>
#include <emmintrin.h>
#include <vector>

std::vector<int> add(std::vector<int> a, std::vector<int> b)
{
	std::vector<int> return_vec(a.size());

       //marhale 1 tabdil be __m128i* 
	__m128i* src1 = reinterpret_cast<__m128i*>(a.data());
	__m128i* src2 = reinterpret_cast<__m128i*>(b.data());
	__m128i* dst   = reinterpret_cast<__m128i*>(return_vec.data());

	for (int i = 0; i < a.size(); i += 4){//chon 128bit yeja jam mishe 4ta 4ta mirim jolo
		__m128i lhs = _mm_loadu_si128(src1 + i);//enteghal az vector be union
		__m128i rhs = _mm_loadu_si128(src2 + i);
		__m128i res = _mm_add_epi32(lhs, rhs);//jam zadan 2 union ba estefade az register haye XMM
		_mm_storeu_si128(dst + i, res);//zakhire union dakhele vector result

		//ya beshekl feshorde zir !
		//_mm_storeu_si128(dst + i, _mm_add_epi32(_mm_loadu_si128(src1 + i), _mm_loadu_si128(src2 + i)));
	}

	return return_vec;
}

int main()
{
	std::vector<int> a = { 1, 5, 6, 2 };
	std::vector<int> b = { 4, 2, 3, 1 };
	std::vector<int> res = add(a, b);
	for (auto i : res){
		std::cout << i << ' ';
	}
	return 0;
}

 

ویرایش :‌ضمنا این مثال آخری که من زدم صرفا نمونه بود و درست نیست.  برای استفاده از وکتور باید allocator , deallocator مخصوص که دیتا 16bit alignتولید کنن هم بنویسید و بجای std::vector توی کد بالا از اون استفاده کنید . (چون alignment دروکتور ۱۶bit نیست ) 

پاسخ داده شده شهریور 4, 1393 بوسیله ی BlueBlade (امتیاز 15,315)   15 18 89
ویرایش شده شهریور 4, 1393 بوسیله ی BlueBlade
تفاوت __m128i و __mm128i چیه؟
اشتباه نوشته بودم  __mm128i نداریم
...