2016-02-10 4 views
5

मैं tutorial से पता के साथ स्मृति में एक असंरेखित स्थान के लिए एक वेक्टर स्टोर करने के लिए कि असंरेखित लोड हो रहा है और जैसे कि यह देख सकते हैं भंडारण:कैसे Altivec

//Load a vector from an unaligned location in memory 
__vector unsigned char LoadUnaligned(const unsigned char * src) 
{ 
    __vector unsigned char permuteVector = vec_lvsl(0, src); 
    __vector unsigned char low = vec_ld(0, src); 
    __vector unsigned char high = vec_ld(16, src); 
    return vec_perm(low, high, permuteVector); 
} 

//Store a vector to an unaligned location in memory 
void StoreUnaligned(__vector unsigned char v, __vector unsigned char * dst) 
{ 
    //Load the surrounding area 
    __vector unsigned char low = vec_ld(0, dst); 
    __vector unsigned char high = vec_ld(16, dst); 
    //Prepare the constants that we need 
    __vector unsigned char permuteVector = vec_lvsr(0, (int*) dst); 
    __vector signed char oxFF = vec_splat_s8(-1); 
    __vector signed char ox00 = vec_splat_s8(0); 
    //Make a mask for which parts of the vectors to swap out 
    __vector unsigned char mask = vec_perm(ox00, oxFF, permuteVector); 
    //Right rotate our input data 
    v = vec_perm(v, v, permuteVector); 
    //Insert our data into the low and high vectors 
    low = vec_sel(v, low, mask); 
    high = vec_sel(high, v, mask); 
    //Store the two aligned result vectors 
    vec_st(low, 0, dst); 
    vec_st(high, 16, dst); 
} 

यह भयानक लग रहा है। एक वेक्टर स्टोर करने के लिए काम की इतनी बड़ी मात्रा! और इसमें उचित प्रदर्शन हानि है।

void SomeFuncA(const unsigned char * src, size_t size, unsigned char * dst) 
{ 
    for(size_t i = 0; i < size; i += 16) 
    { 
     __vector unsigned char a = vec_ld(0, src + i); 
     //simple work 
     vec_st(a, 0, dst + i); 
    }  
} 

void SomeFuncU(const unsigned char * src, size_t size, unsigned char * dst) 
{ 
    for(size_t i = 0; i < size; i += 16) 
    { 
     __vector unsigned char a = LoadUnaligned(src + i); 
     //simple work 
     StoreUnaligned(dst + i, a); 
    }  
} 

दूसरा फ़ंक्शन पहले से 3-4 बार धीरे-धीरे काम करता है। चूंकि मैं इनपुट और आउटपुट मेमोरी के संरेखण को नियंत्रित नहीं कर सकता, तो मुझे दोनों संस्करणों को लागू करना होगा। मैं असाइन किए गए केस के प्रदर्शन प्रदर्शन को कम कैसे कर सकता हूं?

उत्तर

4

सबसे पहले मैं यह उल्लेख करना चाहता हूं कि यदि आप Altivec वेक्टर को एक अनलिखित स्मृति में सहेजते हैं तो कई बार आपको केवल शुरुआत और अंत में सरणी के बीच में पिछली मेमोरी स्थिति को सहेजने की आवश्यकता नहीं होती है। तो वहाँ Simd Library में एक उपयोगी समारोह और वर्ग है, जो इस कार्यक्षमता को लागू है:

typedef __vector uint8_t v128_u8; 
const v128_u8 K8_00 = vec_splat_u8(0x00); 
const v128_u8 K8_FF = vec_splat_u8(0xFF); 

template <bool align> inline v128_u8 Load(const uint8_t * p); 

template <> inline v128_u8 Load<false>(const uint8_t * p) 
{ 
    v128_u8 lo = vec_ld(0, p); 
    v128_u8 hi = vec_ld(16, p); 
    return vec_perm(lo, hi, vec_lvsl(0, p));   
}   

template <> inline v128_u8 Load<true>(const uint8_t * p) 
{ 
    return vec_ld(0, p); 
} 

template <bool align> struct Storer; 

template <> struct Storer<true> 
{ 
    template <class T> Storer(T * ptr) 
     :_ptr((uint8_t*)ptr) 
    { 
    } 

    template <class T> inline void First(T value) 
    { 
     vec_st((v128_u8)value, 0, _ptr); 
    } 

    template <class T> inline void Next(T value) 
    { 
     _ptr += 16; 
     vec_st((v128_u8)value, 0, _ptr); 
    } 

    inline void Flush() 
    { 
    } 
private: 
    uint8_t * _ptr; 
}; 

template <> struct Storer<false> 
{ 
    template <class T> inline Storer(T * ptr) 
     :_ptr((uint8_t*)ptr) 
    { 
     _perm = vec_lvsr(0, _ptr); 
     _mask = vec_perm(K8_00, K8_FF, _perm); 
    } 

    template <class T> inline void First(T value) 
    { 
     _last = (v128_u8)value; 
     v128_u8 background = vec_ld(0, _ptr); 
     v128_u8 foreground = vec_perm(_last, _last, _perm); 
     vec_st(vec_sel(background, foreground, _mask), 0, _ptr); 
    } 

    template <class T> inline void Next(T value) 
    { 
     _ptr += 16; 
     vec_st(vec_perm(_last, (v128_u8)value, _perm), 0, _ptr); 
     _last = (v128_u8)value; 
    } 

    inline void Flush() 
    { 
     v128_u8 background = vec_ld(16, _ptr); 
     v128_u8 foreground = vec_perm(_last, _last, _perm); 
     vec_st(vec_sel(foreground, background, _mask), 16, _ptr); 
    } 
private: 
    uint8_t * _ptr; 
    v128_u8 _perm; 
    v128_u8 _mask; 
    v128_u8 _last; 
}; 

इसका उपयोग करते हुए देखने के लिए किया जाएगा की तरह:

template<bool align> void SomeFunc(const unsigned char * src, size_t size, unsigned char * dst) 
{ 
    Storer<align> _dst(dst); 
    __vector unsigned char a = Load<align>(src); 
    //simple work 
    _dst.First(a);// save first block 
    for(size_t i = 16; i < size; i += 16) 
    { 
     __vector unsigned char a = Load<align>(src + i); 
     //simple work 
     _dst.Next(a);// save body 
    } 
    _dst.Flush(); // save tail  
} 

प्रदर्शन नुकसान हो जाएगा 30-40% संरेखित से तुलना संस्करण। यह निश्चित रूप से बेकार है लेकिन सहिष्णुता से है।

अतिरिक्त लाभ कोड को कम कर रहा है - सभी कार्यों (गठबंधन और unaligned) एक ही कार्यान्वयन है।

+0

धन्यवाद! ये बहुत अच्छा दिखता है! – Georg