विशाल सरणियों को भरने के बिना एक बड़ी समस्या का उत्पादन करने का पेशेवर तरीका: सी ++, एक सरणी के हिस्से से मुक्त स्मृति


20

मैं एक भौतिकी सिमुलेशन विकसित कर रहा हूं, और जैसा कि मैं प्रोग्रामिंग में नया हूं, मैं बड़े कार्यक्रमों (मुख्य रूप से मेमोरी मुद्दों) का उत्पादन करते समय समस्याओं में भाग रहा हूं। मुझे डायनामिक मेमोरी एलोकेशन और डिलीट (नए / डिलीट वगैरह) के बारे में पता है, लेकिन मुझे इस बात का बेहतर तरीका चाहिए कि मैं प्रोग्राम को कैसे स्ट्रक्चर करूं।

मान लीजिए कि मैं एक प्रयोग का अनुकरण कर रहा हूं जो कुछ दिनों के लिए चल रहा है, जिसमें बहुत बड़ी नमूना दर है। मुझे एक अरब नमूनों का अनुकरण करना होगा, और उन पर चलना होगा।

सुपर-सरलीकृत संस्करण के रूप में, हम कहेंगे कि एक प्रोग्राम वोल्टास वी [i] लेता है, और उन्हें पत्नियों में गाया जाता है:

यानी न्यूवी [0] = वी [0] + वी [1] + वी [2] + वी [3] + वी [4]

फिर न्यूवी [1] = वी [१] + वी [२] + वी [३] + वी [४] + वी [५]

तब न्यूवी [2] = वी [2] + वी [3] + वी [4] + वी [5] + वी [6] ... और यह एक अरब नमूनों के लिए जाता है।

अंत में, मेरे पास V [0], V [1], ..., V [1000000000] होगा, जब इसके बजाय केवल अगले चरण के लिए मुझे स्टोर करने की आवश्यकता होगी, अंतिम 5 V [i] रों।

मैं सरणी के उस हिस्से को कैसे हटा / हटाऊंगा जिससे मेमोरी फिर से उपयोग करने के लिए स्वतंत्र हो (उदाहरण के पहले भाग के बाद V [0] जहां इसकी अब आवश्यकता नहीं है)? क्या ऐसे कार्यक्रम की संरचना करने के विकल्प हैं?

मैंने मॉलोक / मुक्त के बारे में सुना है, लेकिन सुना है कि उनका उपयोग C ++ में नहीं किया जाना चाहिए और यह कि बेहतर विकल्प हैं।

बहुत बहुत धन्यवाद!

tldr; सरणियों के हिस्सों (व्यक्तिगत तत्वों) के साथ क्या करना है मुझे अब और ज़रूरत नहीं है कि स्मृति की एक बड़ी राशि ले रहे हैं?


2
आप किसी ऐरे के हिस्से को नहीं हटा सकते। आप इसे कहीं और छोटे सरणी में फिर से आवंटित कर सकते हैं, लेकिन यह महंगा साबित हो सकता है। आप संभवतः एक अलग डेटा संरचना का उपयोग कर सकते हैं जैसे कि लिंक की गई सूची, संभवतः। हो सकता है कि आप चरणों को Vएक नए सरणी में संग्रहीत कर सकें । मौलिक रूप से, हालांकि, मुझे लगता है कि आपका मुद्दा या तो आपके एल्गोरिदम या आपके डेटा संरचनाओं में है, और चूंकि हमारे पास कोई विवरण नहीं है, इसलिए यह जानना मुश्किल है कि इसे कुशलता से कैसे किया जाए।
विन्सेंट सवार्द

4
साइड नोट: SMA की मनमानी लंबाई की गणना इस पुनरावृत्ति संबंध के साथ विशेष रूप से तेजी से की जा सकती है: NewV [n] = NewV [n-1] - V [n-1] + V [n + 4] (आपका अंकन)। लेकिन ध्यान रखें कि ये विशेष रूप से उपयोगी फिल्टर नहीं हैं। उनकी आवृत्ति प्रतिक्रिया एक ईमानदार है, जो बहुत ज्यादा कभी नहीं है जो आप चाहते हैं (वास्तव में उच्च साइडेलोबेस)।
स्टीव कॉक्स

2
एसएमए = सरल चलती औसत, किसी को भी आश्चर्य हो।
चार्ल्स

3
@SteveCox, जिस तरह से उन्होंने इसे लिखा, उसे एक एफआईआर फ़िल्टर मिला। आपका पुनरावृत्ति समकक्ष IIR फॉर्म है। किसी भी तरह से, आपको अंतिम एन रीडिंग के एक परिपत्र बफर को बनाए रखने के लिए मिलता है।
जॉन आर। स्ट्रॉहम

@ JohnR.Strohm आवेग प्रतिक्रिया समान है, और परिमित है
स्टीव कॉक्स

जवाबों:


58

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

आप अपने एकत्र किए गए डेटा को रख लेते हैं, कि आप डिस्क पर नीचे की ओर जा रहे हैं।

आपके वातावरण के आधार पर, यह उन जगहों में से एक हो सकता है जहां आप अनुभवी मदद पाने से बेहतर हैं। एक विश्वविद्यालय में, आप कंप्यूटर साइंस डिपार्टमेंट में बुलेटिन बोर्ड पर एक नोट डालते हैं, जो आपके डेटा को कम करने में मदद करने के लिए कुछ घंटों के काम के लिए छात्र मजदूरी (या यहां तक ​​कि छात्र परामर्श दरों) की पेशकश करते हैं। या हो सकता है कि आप स्नातक अनुसंधान अवसर अंक प्रदान करते हैं। या कुछ और।


6
एक परिपत्र बफर वास्तव में मैं क्या देख रहा हूँ लगता है! मैंने अब बूस्ट C ++ लाइब्रेरियों को स्थापित किया है और इसमें बूस्ट / सर्कुलर_बफ़र.हैप शामिल किया है, और उम्मीद के मुताबिक काम कर रहा है। धन्यवाद, @ जॉन
Drummermean

2
केवल बहुत ही कम एफआईआर फिल्टर सॉफ्टवेयर में प्रत्यक्ष रूप में लागू किए जाते हैं, और एसएमए लगभग कभी नहीं होते हैं।
स्टीव कॉक्स

@SteveCox: आपके द्वारा उपयोग किए जाने वाले किनारों-में-विंडो सूत्र पूर्णांक और फिक्स्ड-पॉइंट फ़िल्टर के लिए काफी प्रभावी है, हालांकि यह फ़्लोटिंग-पॉइंट के लिए गलत है, जहां संचालन सराहनीय नहीं है।
बेन वोइगट

@BenVoigt मुझे लगता है कि आप मेरी अन्य टिप्पणी का जवाब देना चाहते हैं, लेकिन हां, यह फ़ॉर्म एक परिमाण के चारों ओर एक सीमा चक्र का परिचय देता है जो बहुत मुश्किल हो सकता है। शुक्र है कि हालांकि, यह विशेष सीमा चक्र स्थिर होता है।
स्टीव कॉक्स

आपको वास्तव में उस उपयोग के लिए एक वृत्ताकार बफर के लिए बढ़ावा देने की आवश्यकता नहीं है आप जरूरत से ज्यादा मेमोरी का उपयोग करेंगे।
गेमडेवलर

13

अप्रत्यक्ष के अतिरिक्त स्तर को जोड़कर हर समस्या का हल किया जा सकता है। तो ऐसा करो।

आप C ++ में किसी सरणी का भाग हटा नहीं सकते। लेकिन आप एक नया ऐरे बना सकते हैं, जिसमें सिर्फ वही डेटा रखा जा सकता है जिसे आप रखना चाहते हैं, फिर पुराने को हटा दें। तो आप एक डेटा संरचना बना सकते हैं जो आपको उन तत्वों को "हटाने" की अनुमति देता है जो आप सामने से नहीं चाहते हैं। यह वास्तव में क्या करेगा एक नया सरणी बनाता है और असामाजिक तत्वों को नए में कॉपी करें, फिर पुराने को हटा दें।

या आप बस इस्तेमाल कर सकते हैं std::deque, जो प्रभावी रूप से पहले से ही ऐसा कर सकता है। deque, या "डबल-एंडेड कतार", एक डेटा संरचना है जो उन मामलों के लिए अभिप्रेत है जहां आप तत्वों को दूसरे से जोड़ते हुए एक छोर से हटा रहे हैं।


30
अप्रत्यक्ष स्तर के अतिरिक्त स्तर को जोड़कर हर समस्या का समाधान किया जा सकता है ... सिवाय अप्रत्यक्ष के कई स्तरों के।
YSC

17
@ वाईएससी: और वर्तनी :)
दौड़ मोनिका

1
इस विशेष समस्या के std::dequeलिए जाने का रास्ता है
davidbak

7
@ दाविदबक - क्या? स्मृति को लगातार आवंटित करने और जारी करने की आवश्यकता नहीं है। एक निश्चित आकार का वृत्ताकार बफर जिसे आरंभीकरण के समय एक बार आवंटित किया जाता है, इस समस्या के लिए बहुत बेहतर है।
डेविड हैमेन

2
@David Hammen: शायद, लेकिन 1) मानक पुस्तकालय में अपने टूलकिट में "निश्चित आकार के परिपत्र बफर" नहीं है। 2) यदि आपको वास्तव में इस तरह के अनुकूलन की आवश्यकता है, तो आप वास्तविक आवंटन को कम करने के लिए कुछ एलोकेटर सामान कर सकते हैं deque। अनुरोध के अनुसार, भंडारण और पुन: उपयोग का आवंटन। तो dequeसमस्या का पूरी तरह से पर्याप्त समाधान लगता है।
निकोल बोलस

4

आपके द्वारा प्राप्त एफआईआर और एसएमए उत्तर आपके मामले में अच्छे हैं, हालांकि मैं एक और सामान्य दृष्टिकोण को आगे बढ़ाने का अवसर लेना चाहता हूं।

आपके पास यहां डेटा की एक धारा है: अपने प्रोग्राम को 3 बड़े चरणों में संरचित करने के बजाय (डेटा प्राप्त करें, गणना करें, आउटपुट परिणाम), जो एक ही बार में सभी डेटा को लोड करने की आवश्यकता होती है, आप इसके बजाय इसे पाइपलाइन के रूप में संरचना कर सकते हैं ।

एक पाइपलाइन एक धारा के साथ शुरू होती है, इसे बदल देती है, और इसे एक सिंक में धकेल देती है।

आपके मामले में, पाइपलाइन ऐसी दिखती है:

  1. डिस्क से आइटम पढ़ें, आइटम को एक बार में छोड़ें
  2. प्राप्त आइटम को एक बार में प्राप्त करें, प्रत्येक आइटम के लिए प्राप्त अंतिम 5 प्राप्त करें (जहां आपका परिपत्र बफर आता है)
  3. प्रत्येक समूह को परिणाम की गणना के लिए एक समय में आइटम 5 प्राप्त करें
  4. परिणाम प्राप्त करें, इसे डिस्क पर लिखें

C ++ धाराओं के बजाय पुनरावृत्तियों का उपयोग करने के लिए जाता है, लेकिन ईमानदार धाराएँ मॉडल के लिए आसान हैं ( ऐसी श्रेणियों के लिए एक प्रस्ताव है जो धाराओं के समान होगा):

template <typename T>
class Stream {
public:
    virtual boost::optional<T> next() = 0;
    virtual ~Stream() {}
};

class ReaderStream: public Stream<Item> {
public:
    boost::optional<Item> next() override final;

private:
    std::ifstream file;
};

class WindowStream: public Stream<Window> {
public:
    boost::optional<Window> next() override final;

private:
    Window window;
    Stream<Item>& items;
};

class ResultStream: public Stream<Result> {
public:
    boost::optional<Result> next() override final;

private:
    Stream<Window>& windows;
};

और फिर, पाइपलाइन जैसी दिखती है:

ReaderStream itemStream("input.txt");
WindowStream windowStream(itemsStream, 5);
ResultStream resultStream(windowStream);
std::ofstream results("output.txt", std::ios::binary);

while (boost::optional<Result> result = resultStream.next()) {
    results << *result << "\n";
}

धाराएँ हमेशा लागू नहीं होती हैं (वे काम नहीं करती हैं जब आपको डेटा तक यादृच्छिक पहुँच की आवश्यकता होती है), लेकिन जब वे होती हैं, तो वे रॉक करती हैं: बहुत कम मात्रा में मेमोरी पर काम करके आप इसे सीपीयू कैश में रखते हैं।


एक अन्य नोट पर: ऐसा लगता है कि आपकी समस्या "शर्मनाक समानांतर" हो सकती है, आप अपनी बड़ी फ़ाइल को चंक्स में विभाजित करना चाहते हैं (ध्यान रखें, 5 की खिड़कियों द्वारा प्रसंस्करण के लिए, आपको प्रत्येक सीमा पर 4 सामान्य तत्व होने चाहिए) और फिर समानांतर में विखंडू को संसाधित करें।

यदि सीपीयू अड़चन है (और I / O नहीं), तो आप इसे प्रति कोर एक प्रक्रिया शुरू करके गति कर सकते हैं जो आपके पास फ़ाइलों को लगभग बराबर मात्रा में विभाजित करने के बाद है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.