سؤال

كم يقرأ من ThreadLocal متغير أبطأ من المجال العادي؟

وبشكل أكثر تحديدًا، فإن إنشاء كائن بسيط يكون أسرع أو أبطأ من الوصول إليه ThreadLocal عامل؟

وأفترض أنه سريع بما فيه الكفاية بحيث يكون ThreadLocal<MessageDigest> المثيل أسرع بكثير من إنشاء مثيل MessageDigest كل مرة.ولكن هل ينطبق ذلك أيضًا على البايت[10] أو البايت[1000] على سبيل المثال؟

يحرر:السؤال هو ما الذي يحدث بالفعل عند الاتصال ThreadLocalالحصول على؟إذا كان هذا مجرد مجال، مثل أي مجال آخر، فستكون الإجابة "إنه الأسرع دائمًا"، أليس كذلك؟

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

المحلول

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

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

وبناء MessageDigest من المرجح أن تكون مكلفة نسبيا. أنه يحتوي على قدر لا بأس به من الدولة وبناء يمر عبر Provider آلية SPI. قد تكون قادرة على تحسين بواسطة، على سبيل المثال، استنساخ أو توفير Provider.

وفقط لأنه قد يكون أسرع إلى مخبأ في ThreadLocal بدلا من إنشاء هذا لا يعني بالضرورة أن أداء النظام سوف تزيد. سيكون لديك النفقات الإضافية المتعلقة GC التي تبطئ كل شيء إلى أسفل.

وإذا لم التطبيق يستخدم بشكل كبير جدا MessageDigest قد ترغب في النظر في استخدام ذاكرة التخزين ذات ألوان تقليدية بدلا من ذلك.

نصائح أخرى

في عام 2009، بعض JVMs نفذت ThreadLocal باستخدام HashMap unsynchronised في كائن Thread.currentThread (). وهذا جعل من سريع للغاية (على الرغم من عدم تقريبا بالسرعة باستخدام الوصول ميدانية منتظمة، وبطبيعة الحال)، وكذلك ضمان أن الكائن ThreadLocal حصلت على تسويتها حتى عندما مات الموضوع. تحديث هذه الإجابة في عام 2016، على ما يبدو معظم (جميع؟) JVMs أحدث تستخدم ThreadLocalMap مع خطي تحقيقا. ولست متأكدا أداء تلك - ولكن لا أستطيع أن أتخيل أنها ليست أسوأ بكثير من تطبيق السابقة

.

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

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

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

var cnt: String = ""
val tlocal = new java.lang.ThreadLocal[String] {
  override def initialValue = ""
}

def loop_heap_write = {                                                                                                                           
  var i = 0                                                                                                                                       
  val until = totalwork / threadnum                                                                                                               
  while (i < until) {                                                                                                                             
    if (cnt ne "") cnt = "!"                                                                                                                      
    i += 1                                                                                                                                        
  }                                                                                                                                               
  cnt                                                                                                                                          
} 

def threadlocal = {
  var i = 0
  val until = totalwork / threadnum
  while (i < until) {
    if (tlocal.get eq null) i = until + i + 1
    i += 1
  }
  if (i > until) println("thread local value was null " + i)
}

متاح هنا, ، تم إجراؤها على معالج AMD 4x 2.8 جيجا هرتز ثنائي النواة ومعالج i7 رباعي النواة مع تقنية Hyperthreading (2.67 جيجا هرتز).

هذه هي الأرقام:

i7

المواصفات:Intel i7 2x Quad-core @ 2.67 GHz اختبار:scala.threads.ParallelTests

اسم الاختبار:loop_heap_read

رقم الموضوع:1 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 9.0069 9.0036 9.0017 9.0084 9.0074 (AVG = 9.1034 دقيقة = 8.9986 كحد أقصى = 21.0306)

رقم الموضوع:2 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 4.5563 4.7128 4.5663 4.5617 4.5724 (AVG = 4.6337 دقيقة = 4.5509 كحد أقصى = 13.9476)

رقم الموضوع:4 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 2.3946 2.3979 2.3934 2.3937 2.3964 (AVG = 2.5113 دقيقة = 2.3884 كحد أقصى = 13.5496)

رقم الموضوع:8 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 2.4479 2.4362 2.4323 2.4472 2.4383 (AVG = 2.5562 دقيقة = 2.4166 كحد أقصى = 10.3726)

اسم الاختبار:Threadlocal

رقم الموضوع:1 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 91.1741 90.8978 90.6181 90.6200 90.6113 (AVG = 91.0291 دقيقة = 90.6000 كحد أقصى = 129.7501)

رقم الموضوع:2 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 45.3838 45.3858 45.6676 45.3772 45.3839 (avg = 46.0555 دقيقة = 45.3726 كحد أقصى = 90.7108)

رقم الموضوع:4 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 22.8118 22.8135 59.1753 22.8229 22.8172 (AVG = 23.9752 دقيقة = 22.7951 كحد أقصى = 59.1753)

رقم الموضوع:8 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 22.2965 22.2415 22.3438 22.3109 22.4460 (AVG = 23.2676 دقيقة = 22.2346 كحد أقصى = 50.3583)

أيه إم دي

المواصفات:AMD 8220 4x Dual-Core @ 2.8 GHz اختبار:scala.threads.ParallelTests

اسم الاختبار:loop_heap_read

إجمالي العمل:20000000 Thread Num.:1 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 12.625 12.631 12.634 12.632 12.628 (AVG = 12.7333 دقيقة = 12.619 كحد أقصى = 26.698)

اسم الاختبار:LOOP_HEAP_READ العمل الكلي:20000000

أوقات التشغيل:(يظهر آخر 5) 6.412 6.424 6.408 6.397 6.43 (AVG = 6.5367 دقيقة = 6.393 كحد أقصى = 19.716)

رقم الموضوع:4 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 3.385 4.298 9.7 6.535 3.385 (AVG = 5.6079 دقيقة = 3.354 كحد أقصى = 21.603)

رقم الموضوع:8 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 5.389 5.795 10.818 3.823 3.824 (AVG = 5.5810 دقيقة = 2.405 كحد أقصى = 19.755)

اسم الاختبار:Threadlocal

رقم الموضوع:1 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 200.217 207.335 200.241 207.342 200.23 (AVG = 202.2424 دقيقة = 200.184 كحد أقصى = 245.369)

رقم الموضوع:2 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 100.208 100.199 100.211 103.781 100.215 (AVG = 102.2238 دقيقة = 100.192 كحد أقصى = 129.505)

رقم الموضوع:4 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 62.101 67.629 62.087 52.021 55.766 (AVG = 65.6361 دقيقة = 50.282 كحد أقصى = 167.433)

رقم الموضوع:8 اختبارات إجمالية:200

أوقات التشغيل:(يظهر آخر 5) 40.672 74.301 34.434 41.549 28.119 (AVG = 54.7701 min = 28.119 Max = 94.424)

ملخص

يبلغ حجم مؤشر الترابط المحلي حوالي 10-20x من قراءة الكومة.يبدو أيضًا أنه يتوسع بشكل جيد في تطبيق JVM وهذه البنى مع عدد المعالجات.

وهنا يذهب اختبار آخر.أظهرت النتائج أن ThreadLocal أبطأ قليلاً من الحقل العادي، ولكن بنفس الترتيب.أبطأ بنسبة 12% تقريبًا

public class Test {
private static final int N = 100000000;
private static int fieldExecTime = 0;
private static int threadLocalExecTime = 0;

public static void main(String[] args) throws InterruptedException {
    int execs = 10;
    for (int i = 0; i < execs; i++) {
        new FieldExample().run(i);
        new ThreadLocaldExample().run(i);
    }
    System.out.println("Field avg:"+(fieldExecTime / execs));
    System.out.println("ThreadLocal avg:"+(threadLocalExecTime / execs));
}

private static class FieldExample {
    private Map<String,String> map = new HashMap<String, String>();

    public void run(int z) {
        System.out.println(z+"-Running  field sample");
        long start = System.currentTimeMillis();
        for (int i = 0; i < N; i++){
            String s = Integer.toString(i);
            map.put(s,"a");
            map.remove(s);
        }
        long end = System.currentTimeMillis();
        long t = (end - start);
        fieldExecTime += t;
        System.out.println(z+"-End field sample:"+t);
    }
}

private static class ThreadLocaldExample{
    private ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<Map<String,String>>() {
        @Override protected Map<String, String> initialValue() {
            return new HashMap<String, String>();
        }
    };

    public void run(int z) {
        System.out.println(z+"-Running thread local sample");
        long start = System.currentTimeMillis();
        for (int i = 0; i < N; i++){
            String s = Integer.toString(i);
            myThreadLocal.get().put(s, "a");
            myThreadLocal.get().remove(s);
        }
        long end = System.currentTimeMillis();
        long t = (end - start);
        threadLocalExecTime += t;
        System.out.println(z+"-End thread local sample:"+t);
    }
}
}'

انتاج:

0- تشغيل العينة الميدانية

0-عينة نهاية الحقل:6044

0-تشغيل العينة المحلية للخيط

0-نهاية الخيط العينة المحلية: 6015

1- تشغيل العينة الميدانية

1-عينة المجال النهائي:5095

1- تشغيل العينة المحلية للخيط

1-نهاية الخيط عينة محلية:5720

2- تشغيل العينة الميدانية

2-نهاية العينة الميدانية:4842

2- تشغيل العينة المحلية للخيط

2-نهاية الخيط العينة المحلية:5835

3- تشغيل العينة الميدانية

3-نهاية العينة الميدانية:4674

3- تشغيل العينة المحلية للخيط

3-نهاية الخيط العينة المحلية:5287

4- تشغيل العينة الميدانية

4-نهاية العينة الميدانية:4849

4- تشغيل العينة المحلية للخيط

4-نهاية الخيط العينة المحلية:5309

5- تشغيل العينة الميدانية

5-نهاية العينة الميدانية:4781

5- تشغيل العينة المحلية للخيط

5-نهاية الخيط العينة المحلية: 5330

6- تشغيل العينة الميدانية

6-نهاية العينة الميدانية:5294

6- تشغيل العينة المحلية للخيط

6-نهاية الخيط العينة المحلية:5511

7- تشغيل العينة الميدانية

7-نهاية العينة الميدانية:5119

7- تشغيل العينة المحلية للخيط

7-نهاية الخيط العينة المحلية:5793

8- تشغيل العينة الميدانية

8-نهاية العينة الميدانية:4977

8- تشغيل العينة المحلية للخيط

8-نهاية الخيط العينة المحلية:6374

9- تشغيل العينة الميدانية

9-نهاية العينة الميدانية:4841

9- تشغيل العينة المحلية للخيط

9-نهاية الخيط العينة المحلية:5471

متوسط ​​الحقل: 5051

متوسط ​​الموضوع المحلي: 5664

البيئة:

نسخة openjdk "1.8.0_131"

وحدة المعالجة المركزية Intel® Core™ i7-7500U بسرعة 2.70 جيجا هرتز × 4

أوبونتو 16.04 إل تي إس

وPete هو الاختبار الصحيح قبل على النحو الأمثل.

وسأكون مندهشا للغاية إذا بناء MessageDigest لديه أي فوق خطيرا بالمقارنة مع أكتاولي استخدامه.

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

وبناء عليه وقياسه.

وأيضا، تحتاج threadlocal واحدة فقط إذا كنت تلخص رسالة سلوكك هضم إلى كائن. إذا كنت في حاجة الى MessageDigest المحلي وبايت المحلي [1000] لبعض الأغراض، وخلق كائن مع messageDigest و[] بايت الميدانية ووضع هذا الكائن في ThreadLocal بدلا من كلا على حدة.

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