سؤال

أنا أعمل على العرف بمناسبة الإفراج نمط مخصص الذاكرة عن د لغة البرمجة التي تعمل عن طريق تخصيص من موضوع المناطق المحلية.يبدو أن التخزين المحلي لمؤشر الترابط عنق الزجاجة تسبب ضخمة (~50%) من التباطؤ في تخصيص الذاكرة من هذه المناطق مقارنة متطابقة واحدة مترابطة نسخة من قانون حتى بعد تصميم بلدي رمز واحد فقط TLS البحث في توزيع/deallocation.ويستند هذا على تخصيص/تحرير عدد كبير من المرات في حلقة و أنا في محاولة لمعرفة إذا كانت قطعة أثرية من القياس الأسلوب.ما أفهمه هو أن التخزين المحلي لمؤشر الترابط ينبغي أن الأساس تنطوي على مجرد الحصول على شيء ما من خلال طبقة إضافية من المراوغة ، على غرار الوصول إلى متغير عن طريق المؤشر.هذا غير صحيح ؟ كم النفقات العامة لا موضوع التخزين المحلي عادة ؟

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

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

المحلول

السرعة تعتمد على TLS التنفيذ.

نعم, أنت محق في أن TLS يمكن أن يكون بأسرع مؤشر البحث.يمكن أن يكون حتى أسرع على أنظمة مع وحدة إدارة الذاكرة.

على المؤشر بحث كنت بحاجة إلى مساعدة من جدولة على الرغم من.جدولة يجب على مهمة التبديل - تحديث المؤشر إلى TLS البيانات.

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

إذا تم جدولة لا يدعم أي من هذه الأساليب ، مترجم/المكتبة ما يلي:

  • الحصول على رقم الموضوع
  • تأخذ إشارة
  • البحث المؤشر إلى TLS كتلة من قبل رقم الموضوع (يمكن استخدام الخريطة أو نحو ذلك)
  • إطلاق إشارة
  • عودة هذا المؤشر.

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

في إشارة هو راجع للشغل المطلوبة للتأكد من عدم ترابط يقرأ من TLS مؤشر القائمة في حين أن مؤشر ترابط آخر في منتصف التفريخ موضوع جديد.(مثل تخصص جديد TLS كتلة وتعديل datastructure).

للأسف فإنه ليس من غير المألوف أن نرى بطء TLS التنفيذ في الممارسة العملية.

نصائح أخرى

والسكان المحليين الموضوع في D وسريع حقا. هنا هي بلدي الاختبارات.

و64 بت أوبونتو، i5 و الأساسية، v2.052 DMD خيارات المترجم: DMD -O -release -inline -m64

// this loop takes 0m0.630s
void main(){
    int a; // register allocated
    for( int i=1000*1000*1000; i>0; i-- ){
        a+=9;
    }
}

// this loop takes 0m1.875s
int a; // thread local in D, not static
void main(){
    for( int i=1000*1000*1000; i>0; i-- ){
        a+=9;
    }
}

وهكذا نخسر 1.2 ثانية فقط واحدة من النوى وحدة المعالجة المركزية في 1000 * 1000 * 1000 موضوع المداخل المحلية. يتم الوصول إلى السكان المحليين الموضوع باستخدام السجل٪ خ م - حتى لا يكون هناك سوى بضعة أوامر معالج المعنية:

وتفكيك مع -d objdump:

- this is local variable in %ecx register (loop counter in %eax):
   8:   31 c9                   xor    %ecx,%ecx
   a:   b8 00 ca 9a 3b          mov    $0x3b9aca00,%eax
   f:   83 c1 09                add    $0x9,%ecx
  12:   ff c8                   dec    %eax
  14:   85 c0                   test   %eax,%eax
  16:   75 f7                   jne    f <_Dmain+0xf>

- this is thread local, %fs register is used for indirection, %edx is loop counter:
   6:   ba 00 ca 9a 3b          mov    $0x3b9aca00,%edx
   b:   64 48 8b 04 25 00 00    mov    %fs:0x0,%rax
  12:   00 00 
  14:   48 8b 0d 00 00 00 00    mov    0x0(%rip),%rcx        # 1b <_Dmain+0x1b>
  1b:   83 04 08 09             addl   $0x9,(%rax,%rcx,1)
  1f:   ff ca                   dec    %edx
  21:   85 d2                   test   %edx,%edx
  23:   75 e6                   jne    b <_Dmain+0xb>

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

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

لمعرفة أي نوع من التعليمات البرمجية يتم إنشاؤها لTLS، ترجمة وobj2asm هذا الرمز:

__thread int x;
int foo() { return x; }

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

ولقد كتبت قبل نحو ترابط التجمع كود تخصيص سنوات، وتخزينها مؤقتا TLS التعامل مع لحمام السباحة، التي عملت بشكل جيد.

إذا لا يمكنك استخدام مترجم دعم TLS، يمكنك إدارة TLS نفسك. لقد بنيت قالب مجمع لC ++، لذلك فمن السهل أن تحل محل التنفيذ الأساسي. في هذا المثال، لقد تنفيذه ل Win32. ملاحظة: لأنك لا تستطيع الحصول على عدد غير محدود من المؤشرات TLS لكل عملية (على الأقل تحت Win32 و)، يجب الإشارة إلى كومة كتل كبيرة بما يكفي لاستيعاب كافة بيانات محددة الموضوع. بهذه الطريقة يكون لديك الحد الأدنى لعدد مؤشرات TLS والاستفسارات ذات الصلة. في "أفضل الأحوال"، وكنت للتو 1 TLS مؤشر يشير إلى كتلة كومة الخاص واحد لكل موضوع.

في وباختصار يمكن القول: لا تشير إلى كائنات واحدة، بدلا من الإشارة إلى ترابط محددة، كومة الذاكرة / الحاويات عقد مؤشرات الكائن لتحقيق أداء أفضل

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

استخدام:

<اقتباس فقرة>   

ولنوع *:   tl_ptr <نوع>

     

ولنوع CONST *:   tl_ptr <نوع CONST>

     

ولنوع * CONST:   CONST tl_ptr <نوع>

     

ونوع CONST * CONST:   CONST tl_ptr <نوع CONST>

template<typename T>
class tl_ptr {
protected:
    DWORD index;
public:
    tl_ptr(void) : index(TlsAlloc()){
        assert(index != TLS_OUT_OF_INDEXES);
        set(NULL);
    }
    void set(T* ptr){
        TlsSetValue(index,(LPVOID) ptr);
    }
    T* get(void)const {
        return (T*) TlsGetValue(index);
    }
    tl_ptr& operator=(T* ptr){
        set(ptr);
        return *this;
    }
    tl_ptr& operator=(const tl_ptr& other){
        set(other.get());
        return *this;
    }
    T& operator*(void)const{
        return *get();
    }
    T* operator->(void)const{
        return get();
    }
    ~tl_ptr(){
        TlsFree(index);
    }
};

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

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

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

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

ولقد شهدنا مشكلات في الأداء مماثلة من TLS (في ويندوز). ونحن نعتمد عليه لبعض العمليات الحيوية داخل منتجاتنا "نواة". وبعد جهد قررت لمحاولة تحسين في هذا الشأن.

ويسرني أن أقول إن لدينا الآن API الصغيرة التي توفر الحد> 50٪ في وقت وحدة المعالجة المركزية لعملية تعادل عندما موضوع callin "لا أعرف" في موضوع الهوية والحد من> 65٪ في حال الدعوة موضوع قد حصلت بالفعل على موضوع الهوية (ربما لبعض خطوة من خطوات التجهيز السابقة الأخرى).

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

والكل في الكل أعتقد أن الدعم Win32 وTLS هو ضعيف وضعت حقا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top