मुझे लगता है कि सोबेल ऑपरेटर कोड को अनुकूलित करने के लिए ट्रांसपोज़/2-पास अच्छा नहीं है। सोबेल ऑपरेटर कम्प्यूटेशनल फ़ंक्शन नहीं है, इसलिए इस मामले के लिए स्थानांतरण/2-पास पहुंच के लिए मेमोरी एक्सेस बर्बाद करना अच्छा नहीं है। मैंने कुछ सोबेल ऑपरेटर टेस्ट कोड लिखे हैं यह देखने के लिए कि एसएसई कितनी तेजी से प्राप्त कर सकता है। यह कोड पहले और अंतिम किनारे पिक्सेल को संभाल नहीं करता है, और sqrt() मान की गणना करने के लिए एफपीयू का उपयोग करता है।
सोबेल ऑपरेटर को 14 गुणा, 1 वर्ग रूट, 11 अतिरिक्त, 2 मिनट/अधिकतम, 12 पढ़ने का उपयोग और 1 लेखन पहुंच ऑपरेटर की आवश्यकता है। इसका मतलब है कि यदि आप कोड को अच्छी तरह अनुकूलित करते हैं तो आप 20 ~ 30 चक्र में एक घटक को संसाधित कर सकते हैं।
फ़्लोटसोबेल() फ़ंक्शन ने 256 * 256 छवि प्रसंस्करण 32.76 चक्र/घटक को संसाधित करने के लिए 2113044 CPU चक्र लिया। मैं इस नमूना कोड को एसएसई में बदल दूंगा।
void FPUSobel()
{
BYTE* image_0 = g_image + g_image_width * 0;
BYTE* image_1 = g_image + g_image_width * 1;
BYTE* image_2 = g_image + g_image_width * 2;
DWORD* screen = g_screen + g_screen_width*1;
for(int y=1; y<g_image_height-1; ++y)
{
for(int x=1; x<g_image_width-1; ++x)
{
float gx = image_0[x-1] * (+1.0f) +
image_0[x+1] * (-1.0f) +
image_1[x-1] * (+2.0f) +
image_1[x+1] * (-2.0f) +
image_2[x-1] * (+1.0f) +
image_2[x+1] * (-1.0f);
float gy = image_0[x-1] * (+1.0f) +
image_0[x+0] * (+2.0f) +
image_0[x+1] * (+1.0f) +
image_2[x-1] * (-1.0f) +
image_2[x+0] * (-2.0f) +
image_2[x+1] * (-1.0f);
int result = (int)min(255.0f, max(0.0f, sqrtf(gx * gx + gy * gy)));
screen[x] = 0x01010101 * result;
}
image_0 += g_image_width;
image_1 += g_image_width;
image_2 += g_image_width;
screen += g_screen_width;
}
}
SseSobel() फ़ंक्शन एक ही 256 * 256 छवि पर कार्रवाई करने 613,220 सीपीयू चक्र ले लिया। इसमें 9.51 चक्र/घटक और FPUSobel() से 3.4 गुना तेजी से लिया गया। ऑप्टिमाइज़ करने के लिए कुछ रिक्त स्थान हैं लेकिन यह 4 गुना से अधिक तेज़ नहीं होगा क्योंकि यह 4-तरफा सिमड का उपयोग करता है।
यह फ़ंक्शन एक बार में 4 पिक्सल को संसाधित करने के लिए सोए दृष्टिकोण का उपयोग करता था। एसओए अधिकांश सरणी या छवि डेटा में एओएस से बेहतर है क्योंकि आपको एओएस का उपयोग करने के लिए ट्रांसफर/शफल करना होगा। और एसएएस कोडों के लिए सामान्य सी कोड को बदलना बहुत आसान है।
void SseSobel()
{
BYTE* image_0 = g_image + g_image_width * 0;
BYTE* image_1 = g_image + g_image_width * 1;
BYTE* image_2 = g_image + g_image_width * 2;
DWORD* screen = g_screen + g_screen_width*1;
__m128 const_p_one = _mm_set1_ps(+1.0f);
__m128 const_p_two = _mm_set1_ps(+2.0f);
__m128 const_n_one = _mm_set1_ps(-1.0f);
__m128 const_n_two = _mm_set1_ps(-2.0f);
for(int y=1; y<g_image_height-1; ++y)
{
for(int x=1; x<g_image_width-1; x+=4)
{
// load 16 components. (0~6 will be used)
__m128i current_0 = _mm_unpacklo_epi8(_mm_loadu_si128((__m128i*)(image_0+x-1)), _mm_setzero_si128());
__m128i current_1 = _mm_unpacklo_epi8(_mm_loadu_si128((__m128i*)(image_1+x-1)), _mm_setzero_si128());
__m128i current_2 = _mm_unpacklo_epi8(_mm_loadu_si128((__m128i*)(image_2+x-1)), _mm_setzero_si128());
// image_00 = { image_0[x-1], image_0[x+0], image_0[x+1], image_0[x+2] }
__m128 image_00 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(current_0, _mm_setzero_si128()));
// image_01 = { image_0[x+0], image_0[x+1], image_0[x+2], image_0[x+3] }
__m128 image_01 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_srli_si128(current_0, 2), _mm_setzero_si128()));
// image_02 = { image_0[x+1], image_0[x+2], image_0[x+3], image_0[x+4] }
__m128 image_02 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_srli_si128(current_0, 4), _mm_setzero_si128()));
__m128 image_10 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(current_1, _mm_setzero_si128()));
__m128 image_12 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_srli_si128(current_1, 4), _mm_setzero_si128()));
__m128 image_20 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(current_2, _mm_setzero_si128()));
__m128 image_21 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_srli_si128(current_2, 2), _mm_setzero_si128()));
__m128 image_22 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_srli_si128(current_2, 4), _mm_setzero_si128()));
__m128 gx = _mm_add_ps(_mm_mul_ps(image_00,const_p_one),
_mm_add_ps(_mm_mul_ps(image_02,const_n_one),
_mm_add_ps(_mm_mul_ps(image_10,const_p_two),
_mm_add_ps(_mm_mul_ps(image_12,const_n_two),
_mm_add_ps(_mm_mul_ps(image_20,const_p_one),
_mm_mul_ps(image_22,const_n_one))))));
__m128 gy = _mm_add_ps(_mm_mul_ps(image_00,const_p_one),
_mm_add_ps(_mm_mul_ps(image_01,const_p_two),
_mm_add_ps(_mm_mul_ps(image_02,const_p_one),
_mm_add_ps(_mm_mul_ps(image_20,const_n_one),
_mm_add_ps(_mm_mul_ps(image_21,const_n_two),
_mm_mul_ps(image_22,const_n_one))))));
__m128 result = _mm_min_ps(_mm_set1_ps(255.0f),
_mm_max_ps(_mm_set1_ps(0.0f),
_mm_sqrt_ps(_mm_add_ps(_mm_mul_ps(gx, gx), _mm_mul_ps(gy,gy)))));
__m128i pack_32 = _mm_cvtps_epi32(result); //R32,G32,B32,A32
__m128i pack_16 = _mm_packs_epi32(pack_32, pack_32); //R16,G16,B16,A16,R16,G16,B16,A16
__m128i pack_8 = _mm_packus_epi16(pack_16, pack_16); //RGBA,RGBA,RGBA,RGBA
__m128i unpack_2 = _mm_unpacklo_epi8(pack_8, pack_8); //RRGG,BBAA,RRGG,BBAA
__m128i unpack_4 = _mm_unpacklo_epi8(unpack_2, unpack_2); //RRRR,GGGG,BBBB,AAAA
_mm_storeu_si128((__m128i*)(screen+x),unpack_4);
}
image_0 += g_image_width;
image_1 += g_image_width;
image_2 += g_image_width;
screen += g_screen_width;
}
}
यह धागा [क्या सी ++ में एक मैट्रिक्स स्थानांतरित करने के लिए सबसे तेज़ तरीका है?] (Http://stackoverflow.com/questions/16737298/what-is-the-fastest-way-to-transpose-a -मैट्रिक्स-इन-सी) इसमें कुछ अच्छी सामग्री है और आप इसे उपयोगी पा सकते हैं, इनमें से अधिकांश सी –
पर लागू है। निस्संदेह मैं "मेरे दृष्टिकोण को बदलने" का जोखिम नहीं उठा सकता क्योंकि मुझे इन आंकड़ों को सिमड रजिस्टरों में लोड करना होगा। लेकिन ओपनएमपी ... मैं इस भविष्य को पढ़ूंगा। – matovitch
यह। है। महान। ब्लॉक द्वारा एसएसई। मुझे _MM_TRANSPOSE4_PS नहीं पता था जो कि शफल का एक गुच्छा है। एक बार फिर धन्यवाद ! – matovitch