2010-07-09 11 views
11

क्या कोई फ़ंक्शन है (एसएसईएक्स इंट्रिनिक्स ठीक है) जो स्मृति को एक निर्दिष्ट int32_t मान के साथ भर देगा?'int32_t` मान के साथ मेमोरी तेज़ कैसे भरें?

AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00 
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00 
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00 
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00 
... 

मैं std::fill या के लिए लूप सरल इस्तेमाल कर सकते हैं, लेकिन यह तेजी से पर्याप्त नहीं है: उदाहरण के लिए, जब यह मान 0xAABBCC00 के बराबर है के लिए की तरह परिणाम स्मृति दिखना चाहिए।


एक सदिश कार्यक्रम की शुरुआत में केवल एक बार प्रदर्शन किया रीसाइज़िंग, यह कोई मुद्दा नहीं है। बाधा स्मृति भर रहा है।

सरलीकृत कोड:

struct X 
{ 
    typedef std::vector<int32_t> int_vec_t; 
    int_vec_t buffer; 

    X() : buffer(5000000) { /* some more action */ } 
    ~X() { /* some code here */ } 

    // the following function is called 25 times per second 
    const int_vec_t& process(int32_t background, const SOME_DATA& data); 
}; 

const X::int_vec_t& X::process(int32_t background, const SOME_DATA& data) 
{ 
    // the following one string takes 30% of total time of #process function 
    std::fill(buffer.begin(), buffer.end(), background); 

    // some processing 
    // ... 

    return buffer; 
} 
+1

एसएसई निर्देशों का उपयोग करके आप इसे स्वयं क्यों कोड नहीं करते? आपके पास स्मृति को स्थानांतरित करने के लिए movxxxx निर्देश हैं (एक समय में 128 बिट्स)। यह केवल एक पाश और चाल है, इसे करना मुश्किल नहीं होना चाहिए। –

+1

मैं स्वीकार करूंगा कि मैं थोड़ा उत्सुक हूं कि इसके लिए आपके उपयोग का मामला संभवतः हो सकता है जो लूप को पर्याप्त तेज़ नहीं बनाता है। क्या आप बस स्मृति की जबरदस्त ब्लॉक से निपट रहे हैं? क्या आपका मंच ब्रांचिंग में चूसता है? क्या आप इसे अक्सर एक उच्च प्रदर्शन वाले ऐप में करते हैं, जैसे गेम या कुछ? –

+0

@Alexandre C., एसएसई निर्देश ठीक है, लेकिन मैंने सोचा कि शायद WinAPI में पहले से ही कुछ फ़ंक्शन है। मैं एक पहिया का आविष्कार नहीं करना चाहता हूं। –

उत्तर

4

आपके उत्तरों के लिए सभी को धन्यवाद। मैंने wj32's solution चेक किया है, लेकिन यह std::fill डू के समान समय दिखाता है।

// fill the first quarter by the usual way 
std::fill(buffer.begin(), buffer.begin() + buffer.size()/4, background); 
// copy the first quarter to the second (very fast) 
memcpy(&buffer[buffer.size()/4], &buffer[0], buffer.size()/4*sizeof(background)); 
// copy the first half to the second (very fast) 
memcpy(&buffer[buffer.size()/2], &buffer[0], buffer.size()/2*sizeof(background)); 

उत्पादन कोड एक 4 से जांच जोड़ने buffer.size() विभाज्य है और उस के लिए उपयुक्त हैंडलिंग जोड़ने की जरूरत है में: मेरे वर्तमान समाधान समारोह memcpy की मदद से 4 गुना तेजी से std::fill से (विजुअल स्टूडियो 2008 में) काम करता है ।

9

यह (कृपया इसके बारे में माइक्रोसॉफ्ट सत्ता बहाना) मैं इसे कैसे करना होगा है:

VOID FillInt32(__out PLONG M, __in LONG Fill, __in ULONG Count) 
{ 
    __m128i f; 

    // Fix mis-alignment. 
    if ((ULONG_PTR)M & 0xf) 
    { 
     switch ((ULONG_PTR)M & 0xf) 
     { 
      case 0x4: if (Count >= 1) { *M++ = Fill; Count--; } 
      case 0x8: if (Count >= 1) { *M++ = Fill; Count--; } 
      case 0xc: if (Count >= 1) { *M++ = Fill; Count--; } 
     } 
    } 

    f.m128i_i32[0] = Fill; 
    f.m128i_i32[1] = Fill; 
    f.m128i_i32[2] = Fill; 
    f.m128i_i32[3] = Fill; 

    while (Count >= 4) 
    { 
     _mm_store_si128((__m128i *)M, f); 
     M += 4; 
     Count -= 4; 
    } 

    // Fill remaining LONGs. 
    switch (Count & 0x3) 
    { 
     case 0x3: *M++ = Fill; 
     case 0x2: *M++ = Fill; 
     case 0x1: *M++ = Fill; 
    } 
} 
+2

मुझे दिलचस्पी होगी कि यह std :: fill के साथ प्रदर्शन की तुलना कैसे करें। –

+6

क्षमा करें, मैं एक सादा सी लड़का हूँ। मैं std, fill या std :: fill के बारे में कुछ भी नहीं जानता। – wj32

+0

ताकि आप आंतरिक एसएसई निर्देशों का उपयोग कर सकें ... लेकिन यह किसी भी सभ्य संकलक द्वारा स्वचालित रूप से किया जाता है उदा। जब आप वेनिला फॉर-लूप लिखते हैं तो आपके लिए जीसीसी या आईसीपीसी। तो मुझे लगता है कि कोई ज़रूरत नहीं है। –

5

मैं पूछना: आप निश्चित रूप से std::fill प्रोफाइल है और यह प्रदर्शन बाधा साबित हुआ? मुझे लगता है कि इसे एक बहुत ही कुशल तरीके से कार्यान्वित किया जाएगा, जैसे कि कंपाइलर स्वचालित निर्देशों को स्वचालित रूप से उत्पन्न कर सकता है (उदाहरण के लिए -march जीसीसी पर)।

यदि यह बाधा है, तो अभी भी बहुत अधिक स्मृति (स्पष्ट रूप से ओवर और ओवर) स्थापित करने से बचने के लिए एल्गोरिदमिक रीडिज़ाइन (यदि संभव हो) से बेहतर लाभ प्राप्त करना संभव हो सकता है, इससे कोई फर्क नहीं पड़ता कि यह तंत्र को भरता है तुम इस्तेमाल।

3

आप उपयोग कर

vector<int32_t> myVector; 
myVector.reserve(sizeIWant); 

और फिर एसटीडी का उपयोग माना जाता है :: भरने? या शायद std::vector का कन्स्ट्रक्टर जो तर्क के रूप में लिया जाता है और वस्तुओं को शुरू करने के लिए मूल्य को तर्क देता है?

+0

यह वास्तव में एक अच्छा मुद्दा है। यदि आप एक वेक्टर में शामिल हो रहे हैं, तो आपके ओवरहेड का हिस्सा उस वेक्टर का आकार बदलने में हो सकता है, जो हर बार जब आप अंत में डाले जाते हैं तो नहीं होता है (वेक्टर ऑटो-सामान्य रूप से आपकी अपेक्षा से थोड़ा बड़ा विस्तार करता है) लेकिन यह होगा एक प्रदर्शन हिट करने के लिए अक्सर पर्याप्त हो। एक निश्चित लंबाई पूर्व आवंटित करने के लिए आरक्षित() का उपयोग करें। –

+1

आप 100% सुनिश्चित करने के लिए एक सरणी का भी उपयोग कर सकते हैं आकार बदलने का मुद्दा नहीं है। –

+0

हाँ, मूल रूप से मैंने सोचा कि वह malloc() के साथ किए गए सरणी में मान सेट करने का प्रयास कर रहा था। मैंने यह भी नहीं सोचा था कि यह वेक्टर धीमा कर रहा है उसे धीमा कर रहा है। :) –

0

पूरी तरह से सुनिश्चित नहीं है कि आप पंक्ति में 4 बाइट कैसे सेट करते हैं, लेकिन यदि आप एक बार फिर से एक बाइट के साथ मेमोरी भरना चाहते हैं, तो आप memset का उपयोग कर सकते हैं। स्मृति

की

void * memset (void * ptr, int value, size_t num); 

भरण ब्लॉक निर्धारित मूल्य (एक unsigned char के रूप में व्याख्या) के लिए ptr द्वारा बताया स्मृति के ब्लॉक की पहली संख्या बाइट्स सेट करता है।

+2

मैं एक बाइट के साथ मेमोरी भरना नहीं चाहता। 'Int32_t' में चार बाइट्स हैं। –

0

मान लें कि आप अपनी पृष्ठभूमि के पैरामीटर में मानों की एक सीमित मात्रा में है (या और भी बेहतर, केवल पर), हो सकता है आप एक स्थिर वेक्टर आवंटित करने के लिए प्रयास करना चाहिए, और बस memcpy का उपयोग करें।

const int32_t sBackground = 1234; 
static vector <int32_t> sInitalizedBuffer(n, sBackground); 

    const X::int_vec_t& X::process(const SOME_DATA& data) 
    { 
     // the following one string takes 30% of total time of #process function 
     std::memcpy((void*) data[0], (void*) sInitalizedBuffer[0], n * sizeof(sBackground)); 

     // some processing 
     // ... 

     return buffer; 
    } 
-2

यह थोड़ा गैर पोर्टेबल हो सकता है लेकिन आप एक अतिव्यापी स्मृति प्रतिलिपि इस्तेमाल कर सकते हैं। अपने इच्छित पैटर्न के साथ पहले चार बाइट भरें और memcpy() का उपयोग करें।

int32* p = (int32*) malloc(size); 
*p = 1234; 
memcpy(p + 4, p, size - 4); 

नहीं लगता कि आप बहुत तेजी से

+0

यह समर्थित नहीं है। देखें http://stackoverflow.com/questions/387654/why-is-there-no-z80-like-ldir- कार्यक्षमता-in-c-c-rtl – EvilTeach

+0

यह "समर्थित" नहीं हो सकता है लेकिन यह बनाम 2008 में काम करता है। यदि आवश्यक हो तो मैं स्रोत प्रदान कर सकता हूं। मैं लिंक पेज में जो भी संदर्भ दे रहा हूं उसे भी नहीं मिला। – Jay

+1

ओवरलैपिंग memcpy's एक प्रसिद्ध बग है, और उन्हें अनुशंसा करने के लिए बुरी सलाह है। memmove ओवरलैपिंग क्षेत्रों के लिए उपयोग करने के लिए सही कॉल है। "यह समर्थित नहीं है लेकिन यह काम करता है" टॉम्बस्टोन पर उत्कीर्णन के लिए है। –

0

मैं सिर्फ परीक्षण किया एसटीडी प्राप्त कर सकते हैं :: पूर्ण अनुकूलन के साथ जी ++ से भरना (SSE आदि ..सक्षम):

#include <algorithm> 
#include <inttypes.h> 

int32_t a[5000000]; 

int main(int argc,char *argv[]) 
{ 
    std::fill(a,a+5000000,0xAABBCC00); 
    return a[3]; 
} 

और भीतरी पाश की तरह देखा:

L2: 
    movdqa %xmm0, -16(%eax) 
    addl $16, %eax 
    cmpl %edx, %eax 
    jne L2 

लगता 0xAABBCC00 एक्स 4 की तरह xmm0 में लोड किया गया था और एक समय में ले जाया जा रहा है 16-बाइट।

+0

प्राप्त करते हैं मैं उत्सुक हूं, यह कोड तुलनात्मक रूप से क्यों उपयोग करता है और सशर्त कूद अभी भी 'REPNZ STOS' या इसी तरह से तेज है? – Philipp

+0

@ फिलिप: यह एक समय में 16 बाइट प्रतिलिपि बनाता है। तुलना और सशर्त कूद जरूरी नहीं है। यह संदर्भ पर बहुत निर्भर करता है, अन्य निर्देशों को निष्पादित किया जा रहा है। – jalf

0

बनाम2013 और बनाम2015 एक सादा फॉर-लूप को rep stos निर्देश पर अनुकूलित कर सकता है। यह एक बफर भरने का सबसे तेज़ तरीका है। BTW

namespace std { 
    inline void fill(vector<int>::iterator first, vector<int>::iterator last, int value){ 
     for (size_t i = 0; i < last - first; i++) 
      first[i] = value; 
    } 
} 

: आप इस तरह अपने प्रकार के लिए std::fill निर्दिष्ट कर सकते हैं। संकलक अनुकूलन करने के लिए, बफर को सबस्क्रिप्ट ऑपरेटर द्वारा एक्सेस किया जाना चाहिए।

यह जीसीसी और क्लैंग पर काम नहीं करेगा। वे दोनों एक सशर्त कूद लूप को कोड संकलित करेंगे। यह मूल std::fill के रूप में धीमा चलता है। और हालांकि wchar_t 32-बिट है, wmemset में memset पसंद नहीं है। तो आपको अनुकूलन करने के लिए इकट्ठा कोड लिखना होगा।

संबंधित मुद्दे