سؤال

ما هي بعض الأسباب الجيدة حقا للتخلي عنها std::allocator لصالح حل مخصص؟هل واجهت أي مواقف كان فيها ذلك ضروريًا للغاية للصحة والأداء وقابلية التوسع وما إلى ذلك؟أي أمثلة ذكية حقا؟

لقد كانت المخصصات المخصصة دائمًا إحدى ميزات المكتبة القياسية التي لم أكن في حاجة إليها كثيرًا.كنت أتساءل فقط عما إذا كان بإمكان أي شخص هنا في SO تقديم بعض الأمثلة المقنعة لتبرير وجوده.

هل كانت مفيدة؟

المحلول

وكما أذكر هنا ، رأيت STL مخصصة إنتل TBB ل مخصص تحسين أداء التطبيق بشكل ملحوظ مؤشرات ببساطة عن طريق تغيير واحد

std::vector<T>

إلى

std::vector<T,tbb::scalable_allocator<T> >

(وهذا هو وسيلة سريعة ومريحة لتحويل مخصص لاستخدام أكوام الخيط الخاص أنيق TBB؛ وانظر> وأ href = "http://citeseerx.ist.psu.edu/viewdoc/download؛jsessionid=A2FFF3D179B57EC23B9656993289BE8F؟ دوى = 10.1.1.71.8289 ومندوب = REP1 ونوع = قوات الدفاع الشعبي "يختلط =" noreferrer "> الصفحة 7 في هذه الوثيقة )

نصائح أخرى

وأحد المجالات التي موزعي مخصصة يمكن أن تكون مفيدة لتطوير اللعبة، وخصوصا على لوحات المفاتيح لعبة، لأنها لا تملك إلا كمية صغيرة من الذاكرة وليس المبادلة. على هذه النظم كنت ترغب في التأكد من أن لديك رقابة مشددة على كل نظام فرعي، لذلك هذا النظام دون تمحيص واحد لا يمكن أن يسرق الذاكرة من واحدة حرجة. أشياء أخرى مثل موزعي تجمع يمكن أن تساعد على تقليل تجزئة الذاكرة. يمكنك العثور طويلة، ورقة مفصلة عن الموضوع في:

EASTL

- الفنون الالكترونية مكتبة قالب قياسي

وأنا أعمل على mmap-مخصص التي تسمح ناقلات لاستخدام الذاكرة من ملف الذاكرة المعنونة. الهدف هو أن يكون ناقلات التي تستخدم التخزين التي بشكل مباشر في الذاكرة الظاهرية تعيينها من قبل mmap. مشكلتنا ل تحسين القراءة من الملفات الكبيرة حقا (> 10GB) في الذاكرة مع عدم وجود نسخة النفقات العامة، وبالتالي انا بحاجة الى هذا مخصص مخصص.

وحتى الآن لدي هيكل عظمي لمخصص مخصص (التي تستمد من الأمراض المنقولة جنسيا :: مخصص)، وأعتقد أنه هو بداية جيدة نشير إلى إرسال موزعي الخاصة. لا تتردد في استخدام هذه القطعة من التعليمات البرمجية في أي بالطريقة التي تريدها:

#include <memory>
#include <stdio.h>

namespace mmap_allocator_namespace
{
        // See StackOverflow replies to this answer for important commentary about inheriting from std::allocator before replicating this code.
        template <typename T>
        class mmap_allocator: public std::allocator<T>
        {
public:
                typedef size_t size_type;
                typedef T* pointer;
                typedef const T* const_pointer;

                template<typename _Tp1>
                struct rebind
                {
                        typedef mmap_allocator<_Tp1> other;
                };

                pointer allocate(size_type n, const void *hint=0)
                {
                        fprintf(stderr, "Alloc %d bytes.\n", n*sizeof(T));
                        return std::allocator<T>::allocate(n, hint);
                }

                void deallocate(pointer p, size_type n)
                {
                        fprintf(stderr, "Dealloc %d bytes (%p).\n", n*sizeof(T), p);
                        return std::allocator<T>::deallocate(p, n);
                }

                mmap_allocator() throw(): std::allocator<T>() { fprintf(stderr, "Hello allocator!\n"); }
                mmap_allocator(const mmap_allocator &a) throw(): std::allocator<T>(a) { }
                template <class U>                    
                mmap_allocator(const mmap_allocator<U> &a) throw(): std::allocator<T>(a) { }
                ~mmap_allocator() throw() { }
        };
}

لاستخدام هذا، أن يعلن حاوية STL كما يلي:

using namespace std;
using namespace mmap_allocator_namespace;

vector<int, mmap_allocator<int> > int_vec(1024, 0, mmap_allocator<int>());

ويمكن استخدامه على سبيل المثال لتسجيل الدخول كلما يتم تخصيص الذاكرة. ما هو الضروره هي البنية rebind، وإلا تستخدم الحاويات ناقلات في superclasses تخصيص / DEALLOCATE الأساليب.

تحديث: مخصص تعيين ذاكرة متاح الآن على https://github.com/johannesthoma/mmap_allocator وهو LGPL. لا تتردد في استخدامها لمشاريعك.

وأنا أعمل مع محرك التخزين الخلية يستخدم ج ++ لرمزها. نحن باستخدام مخصص مخصصة لاستخدام نظام ذاكرة الخلية بدلا من التنافس مع الخلية للذاكرة. لأنها تتيح لنا التأكد من أننا نستخدم الذاكرة وتكوين المستخدم الخلية للاستخدام، وليس "اضافية".

ويمكن أن يكون من المفيد استخدام موزعي المخصصة لاستخدام تجمع الذاكرة بدلا من الكومة. هذا مثال واحد ضمن أشياء أخرى كثيرة.

لمعظم الحالات، وهذا هو بالتأكيد التحسين سابق لأوانه. ولكن يمكن أن يكون مفيدا للغاية في سياقات معينة (الأجهزة المدمجة، والألعاب، الخ).

وأنا لم أكتب C ++ كود مع مخصص STL العرف، ولكن يمكنني ان اتصور خادم ويب مكتوب في C ++، والذي يستخدم مخصص مخصص للحذف التلقائي للبيانات المؤقتة المطلوبة للاستجابة لطلب HTTP. مخصص مخصصة يمكن تحرير كافة البيانات المؤقتة في وقت واحد مرة واحدة وقد تم إنشاء الاستجابة.

وآخر المحتملة حالة استخدام لمخصص مخصصة (التي استخدمت) يكتب وحدة الاختبار لإثبات أن ذلك لا يتوقف سلوك الدالة على جزء من مدخلاتها. مخصص العرف يمكن أن تملأ المنطقة الذاكرة مع أي نمط.

عند العمل مع وحدات معالجة الرسومات أو المعالجات المشتركة الأخرى، يكون من المفيد أحيانًا تخصيص هياكل البيانات في الذاكرة الرئيسية في ملف طريقة خاصة.هذا طريقة خاصة يمكن تنفيذ تخصيص الذاكرة في مخصص مخصص بطريقة مناسبة.

السبب وراء فائدة التخصيص المخصص خلال وقت تشغيل المسرع عند استخدام المسرعات هو ما يلي:

  1. من خلال التخصيص المخصص، يتم إخطار وقت تشغيل المسرع أو برنامج التشغيل بكتلة الذاكرة
  2. بالإضافة إلى ذلك، يمكن لنظام التشغيل التأكد من أن كتلة الذاكرة المخصصة مقفلة بالصفحة (يسميها البعض هذا الذاكرة المثبتة)، أي أن النظام الفرعي للذاكرة الظاهرية لنظام التشغيل قد لا يقوم بنقل الصفحة أو إزالتها داخل الذاكرة أو منها
  3. إذا 1.و 2.يتم طلب الانتظار ونقل البيانات بين كتلة الذاكرة المقفلة بالصفحة والمسرّع، ويمكن لوقت التشغيل الوصول مباشرة إلى البيانات الموجودة في الذاكرة الرئيسية لأنه يعرف مكانها ويمكن التأكد من أن نظام التشغيل لم يقم بنقلها/إزالتها
  4. يؤدي هذا إلى حفظ نسخة ذاكرة واحدة قد تحدث مع الذاكرة التي تم تخصيصها بطريقة غير مقفلة للصفحة:يجب نسخ البيانات في الذاكرة الرئيسية إلى منطقة التدريج المقفلة بالصفحة، حيث يمكن للمسرع تهيئة نقل البيانات (من خلال DMA)

وأنا باستخدام موزعي مخصصة هنا؛ يمكنك القول حتى أنه كان للعمل <م> حول أخرى لإدارة الذاكرة الديناميكية المخصصة.

والخلفية: لدينا الزائدة عن malloc، calloc، مجانا، ومتغيرات مختلفة من المشغل جديدة وحذف، ورابط بسعادة يجعل من استخدام STL هذه بالنسبة لنا. وهذا يتيح لنا أن تفعل أشياء مثل التلقائي تجمع صغير الكائن، والكشف عن تسرب، ملء الوك، تعبئة مجانية، وتخصيص الحشو مع الحراس، الانحياز خط التخزين المؤقت لبعض allocs، وتأخر مجانا.

والمشكلة هي، ونحن تشغيل في بيئة جزءا لا يتجزأ - لا توجد ذاكرة كافية في جميع أنحاء لفعلا اكتشاف التسرب يمثل بشكل صحيح على مدى فترة ممتدة. على الأقل، وليس في RAM القياسية - هناك كومة أخرى من ذاكرة الوصول العشوائي المتاحة في أماكن أخرى، من خلال وظائف تخصيص مخصصة

.

والحل: إرسال بريد مخصص مخصص يستخدم كومة طويلة، واستخدامه <م> فقط في الأجزاء الداخلية للبنية تتبع تسرب الذاكرة ... كل شيء آخر الافتراضي إلى الوضع الطبيعي الجديد / حذف الزائدة التي لا تسرب تتبع. هذا يتجنب تعقب تتبع نفسه (ويوفر قليلا من وظائف التعبئة اضافية للغاية، ونحن نعلم حجم العقد تعقب).

ونحن أيضا استخدام هذا للحفاظ على البيانات التنميط تكلفة وظيفة، لنفس السبب. يمكن الكتابة على إدخال كل استدعاء دالة والعودة، وكذلك مفاتيح موضوع، والحصول بسرعة باهظة الثمن. مخصص مخصص يعطي لنا مرة أخرى allocs أصغر في منطقة الذاكرة التصحيح أكبر.

وأنا باستخدام مخصص مخصص لحساب عدد من مخصصات / deallocations في جزء واحد من برنامجي وقياس الوقت الذي يستغرقه. هناك طرق أخرى هذا يمكن تحقيقه ولكن هذه الطريقة مريحة للغاية بالنسبة لي. انها مفيدة بشكل خاص التي يمكن استخدامها مخصص مخصصة لمجموعة فرعية فقط من الحاويات بلدي.

واحد الوضع الأساسي: عند كتابة التعليمات البرمجية التي يجب أن تعمل في وحدة (/ DLL EXE) الحدود، فمن الضروري للحفاظ على المخصصات والحذف الخاص بك يحدث في وحدة واحدة فقط

وأين أنا واجهت هذا كان بنية البرنامج المساعد على ويندوز. ومن الضروري، على سبيل المثال، إذا كنت تمر الأمراض المنقولة جنسيا :: سلسلة عبر الحدود DLL، أن أي إعادة توزيع سلسلة تحدث من كومة حيث نشأ من، NOT كومة في DLL التي قد تكون مختلفة *.

* <م> انها أكثر تعقيدا من هذا الواقع، كما لو كنت تقوم بربط حيوي إلى CRT هذا قد عمل على أي حال. ولكن إذا كان كل DLL له علاقة ثابتة إلى CRT كنت متوجها إلى عالم من الألم، حيث أخطاء تخصيص الوهمية تحدث باستمرار.

ومثال واحد من الزمن أنا ولقد استخدمت هذه تعمل مع أنظمة المدمجة محدودة الموارد جدا. دعونا نقول لديك 2K من ذاكرة الوصول العشوائي المجاني والبرنامج لديه لاستخدام بعض من تلك الذاكرة. تحتاج إلى تخزين نقول 4-5 تسلسل في مكان ما ليس على المكدس وبالإضافة إلى ذلك تحتاج إلى أن يكون وصول دقيق جدا أكثر من حيث الحصول على تخزين هذه الأشياء، وهذا هو الوضع الذي قد ترغب في إرسال مخصص الخاص بك. يمكن للتطبيقات الافتراضية تجزئة الذاكرة، وهذا قد يكون مقبولا إذا لم يكن لديك ذاكرة كافية، ولا يمكن إعادة تشغيل البرنامج.

ومشروع واحد كنت أعمل على كان يستخدم AVR دول مجلس التعاون الخليجي على بعض رقائق تعمل بالطاقة منخفضة. كان لدينا لتخزين 8 تسلسل طول متغير ولكن مع أقصى المعروفة. و تنفيذ مكتبة المعياري للإدارة الذاكرة هو مجمع رفيع حول malloc / الحرة التي يحتفظ من حيث لوضع بنود معها من قبل معلق مسبقا كل كتلة تخصيص الذاكرة مع مؤشر فقط بعد نهاية تلك القطعة المخصصة من الذاكرة. عند تخصيص قطعة جديدة من الذاكرة مخصص قياسي له على المشي على كل من قطعة من الذاكرة للعثور على كتلة التالي المتوفرة حيث الحجم المطلوب من الذاكرة سيكون مناسبا. على منصة سطح المكتب هذا سيكون سريع جدا لهذا قليل من البنود ولكن عليك أن نأخذ في الاعتبار أن بعض هذه ميكروكنترولر بطيئة جدا وبدائي في المقارنة. بالإضافة إلى ذلك كانت القضية تجزئة الذاكرة مشكلة كبيرة هذا يعني كان لدينا حقا أي خيار سوى اتخاذ نهج مختلف.

وإذا ما قمنا به هو لتنفيذ منطقتنا تجمع الذاكرة . كان كل كتلة من الذاكرة كبيرة بما يكفي لتناسب أكبر تسلسل سنكون في حاجة في ذلك. هذا تخصيص كتل الحجم ثابت من الذاكرة في وقت مبكر وتميز والتي كانت كتل الذاكرة المستخدمة حاليا. فعلنا هذا عن طريق الحفاظ على واحد صحيح 8 بت حيث كل بت يمثل إذا تم استخدام كتلة معينة. تداولنا من استخدام الذاكرة هنا لمحاولة جعل العملية برمتها بشكل أسرع، وهو في حالتنا كانت مبررة كما كنا دفع هذه الشريحة متحكم بالقرب انها قدرة المعالجة القصوى.

وهناك عدة مرات أخرى أستطيع أن أرى الكتابة مخصص المخصص الخاص بك في سياق النظم المضمنة، على سبيل المثال إذا كانت الذاكرة لتسلسل ليست في ذاكرة الوصول العشوائي الرئيسي كما قد يكون الحال غالبا في <لأ href = " https://en.wikipedia.org/wiki/Modified_Harvard_architecture "يختلط =" نوفولو "> هذه المنصات .

لالذاكرة المشتركة فمن الأهمية بمكان أن يتم تخزين ليس فقط رئيس الحاويات، ولكن أيضا البيانات التي تحتوي في الذاكرة المشتركة.

وهذا مخصص لل دفعة :: بين العمليات هو مثال جيد. ومع ذلك، كما يمكنك قراءة <لأ href = "http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/allocators_containers.html#interprocess.allocators_containers.containers_explained.containers" يختلط = "نوفولو" > هنا هذا allone لا يكفي، لجعل كافة الحاويات STL الذاكرة المشتركة متوافق (ونظرا لإزاحة رسم الخرائط المختلفة في عمليات مختلفة، مؤشرات قد "كسر").

وصلة المطلوب لCppCon 2015 حديث اندريه ألكساندريسكو على موزعي:

https://www.youtube.com/watch؟v=LIb3L4vKZ7U

والشيء الجميل هو أن مجرد وضع لهم يجعلك تعتقد أفكار كيف سيكون استخدامها: -)

منذ بعض الوقت وجدت هذا الحل مفيدًا جدًا بالنسبة لي: مخصص C++ 11 سريع لحاويات STL.يعمل على تسريع حاويات STL قليلاً على VS2017 (~5x) وكذلك على دول مجلس التعاون الخليجي (~7x).وهو مخصص للأغراض الخاصة يعتمد على تجمع الذاكرة.يمكن استخدامه مع حاويات STL فقط بفضل الآلية التي تطلبها.

وأنا شخصيا استخدام وكي :: مخصص / SmallObject لتحسين استخدام الذاكرة لكائنات صغيرة - أن تظهر كفاءة جيدة وأداء مرضية إذا كان لديك للعمل مع كميات معتدلة من الأجسام الصغيرة جدا (من 1 إلى 256 بايت). ويمكن أن يكون ما يصل الى ~ 30 مرات أكثر كفاءة من معيار C ++ جديدة / حذف تخصيص إذا كنا نتحدث عن تخصيص كميات معتدلة من الأجسام الصغيرة للكثير من مختلف الأحجام. أيضا، هناك حل VC محددة تسمى "QuickHeap"، فهو يجمع أفضل أداء ممكن (تخصيص وعمليات إلغاء تخصيص فقط قراءة وكتابة عنوان كتلة تم تخصيص / عاد إلى كومة على التوالي في ما يصل إلى 99. (9)٪ من الحالات - يعتمد على الإعدادات والتهيئة)، ولكن بتكلفة أحمال ملحوظ - أنه يحتاج إلى مؤشرات في مدى واحد خارج عن كل كتلة ذاكرة جديدة. انها أسرع حل ممكن للعمل مع ضخمة (10 000 ++) كميات من الكائنات التي يتم إنشاؤها وحذفها إذا كنت لا تحتاج إلى مجموعة متنوعة كبيرة من الأحجام وجوه (يخلق حمام سباحة لكل فرد من حجم الجسم، 1-1023 بايت في التطبيق الحالي، لذلك تكاليف التهيئة قد التقليل من زيادة الأداء العام، ولكن يمكن للمرء أن يذهب إلى الأمام وتخصيص / إلغاء تخصيص بعض الكائنات وهمية قبل تطبيق يدخل انها مرحلة الأداء النقدي (ق)).

وهذه المسألة مع معيار C ++ جديد / حذف التنفيذ هو أنه عادة ما تكون مجرد غلاف لC malloc / تخصيص الحرة، وأنها تعمل جيدا للكتل أكبر من الذاكرة، مثل 1024+ بايت. لديها فوق ملحوظ من حيث الأداء، وأحيانا ذاكرة إضافية تستخدم لرسم الخرائط أيضا. لذلك، في معظم الحالات يتم تطبيق مخصص موزعي بطريقة لتعظيم الأداء و / أو تقليل مقدار الذاكرة الإضافية المطلوبة لتخصيص صغيرة (≤1024 بايت) الكائنات.

في محاكاة الرسومات، رأيت مخصصات مخصصة تستخدم لـ

  1. قيود المحاذاة التي std::allocator لم يدعم بشكل مباشر.
  2. تقليل التجزئة عن طريق استخدام مجموعات منفصلة للتخصيصات قصيرة العمر (هذا الإطار فقط) والتخصيصات طويلة العمر.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top