سؤال

أنا أكتب تطبيق جافا خيوط متعددة التي تعمل على معالج نيهالم.ومع ذلك لدي مشكلة أن بدءا من 4 المواضيع أنا تقريبا لا أرى تسريع في طلبي.

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

لقد استعملت -XX:+UseNUMA خيار جفم ، لذلك يجب تخصيص المصفوفات في الذاكرة بالقرب من المواضيع المقابلة.

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

هي موضع تقدير أي مساعدة أو أفكار ، وذلك بفضل.


تحرير

شكرا لكم جميعا على الردود.أرى أنني لم أوضح نفسي جيدا بما فيه الكفاية.

قبل محاولة القضاء على مشاكل التزامن في طلبي ، أجريت اختبارا بسيطا يتحقق من أفضل موازاة ممكنة يمكن تحقيقها.الرمز هو كما يلي:

public class TestMultiThreadingArrayAccess {
    private final static int arrSize = 40000000;

    private class SimpleLoop extends Thread {
        public void run() {
            int array[] = new int[arrSize];
            for (long i = 0; i < arrSize * 10; i++) {
                array[(int) ((i * i) % arrSize)]++; // randomize a bit the access to the array
            }
            long sum = 0;
            for (int i = 0; i < arrSize; i++)
                sum += array[i];
        }
    }

    public static void main(String[] args) {
        TestMultiThreadingArrayAccess test = new TestMultiThreadingArrayAccess();
        for (int threadsNumber : new int[] { 1, 2, 4, 8 }) {
            Statistics timer = new Statistics("Executing " + threadsNumber+ " threads"); // Statistics is a simple helper class that measures the times
            timer.start();
            test.doTest(threadsNumber);
            timer.stop();
            System.out.println(timer.toString());
        }
    }

    public void doTest(int threadsNumber) {
        Thread threads[] = new Thread[threadsNumber];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new SimpleLoop();
            threads[i].start();
        }

        for (int i = 0; i < threads.length; i++)
            try {
                threads[i].join();
            } catch (InterruptedException e) {
            };
    }
}

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

على الجهاز تثبيت 2 رباعية النوى معالجات نيهالم فرط (تماما 16 وحدات المعالجة المركزية) ، وذلك مع 8 المواضيع كل واحد يمكن التقاط انها وحدة المعالجة المركزية حصرا.

عندما حاولت تشغيل هذا الاختبار باستخدام مصفوفة أصغر (20 ألف إدخال) ، كان انخفاض وقت تنفيذ 4 مؤشرات ترابط 7 ٪ و 8 مؤشرات ترابط - 14 ٪ ، وهو أمر مرضي.ولكن عندما أحاول تشغيل الوصول العشوائي على مجموعة كبيرة (40 م إدخالات) تشغيل مرات زيادة كبيرة, لذلك أعتقد أن هناك مشكلة أن أجزاء كبيرة من الذاكرة (لأنها لا تناسب في ذاكرة التخزين المؤقت?) يتم الوصول إليها بطريقة غير فعالة.

هل هناك أي أفكار حول كيفية إصلاح هذا?

آمل أن يوضح هذا السؤال بطريقة أفضل ، شكرا مرة أخرى.

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

المحلول

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

مجرد اختبار العقل, أنت أيضا باستخدام جامع مواز? -XX:+UseParallelGC.أوسينوما ساري المفعول فقط بعد ذلك.

نصائح أخرى

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

إضافة.

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

كما يلاحظ أرتيم ، من الممكن أن يكون لديك مزامنة غير ضرورية.لكنني سأبدأ بتأسيس الحقائق.هل تطبيقك يعمل بشكل أبطأ حقا كما تصف?

هنا مقال ثاقب حول هذا الموضوع: http://codeidol.com/java/java-concurrency/Testing-Concurrent-Programs/Avoiding-Performance-Testing-Pitfalls/

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

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

هناك نوعان من المشاكل المحتملة الواضحة التي تتبادر إلى الذهن.

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

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

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

إذا كان التخزين "للقراءة فقط" هل يمكن أن تعطي كل موضوع نسختها الخاصة التي من شأنها أن تسمح جفم والمعالج لتحسين أكسيس الذاكرة.

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

ربما يمكنك أن تجرب هذا?(يرجى ملاحظة أنه كان علي تعديل الاختبار قليلا (انظر التعليق) لأنه استغرق وقتا طويلا للتشغيل على أجهزتي الضعيفة)

لاحظ أيضا أن تشغيل هذا الاختبار باستخدام -server الخيار.

Test with threadNum 1 took 2095717473 ns
Test with threadNum 1 took 2121744523 ns
Test with threadNum 2 took 2489853040 ns
Test with threadNum 2 took 2465152974 ns
Test with threadNum 4 took 5044335803 ns
Test with threadNum 4 took 5041235688 ns
Test with threadNum 8 took 10279012556 ns
Test with threadNum 8 took 10347970483 ns

كود:

import java.util.concurrent.*;

public class Test{
    private final static int arrSize = 20000000;

    public static void main(String[] args) throws Exception {
        int[] nums = {1,1,2,2,4,4,8,8};//allow hotspot optimization
        for (int threadNum : nums) {
            final CyclicBarrier gate = new CyclicBarrier(threadNum+1);
            final CountDownLatch latch = new CountDownLatch(threadNum);
            ExecutorService exec = Executors.newFixedThreadPool(threadNum);
            for(int i=0; i<threadNum; i++){
                Runnable test = 
                  new Runnable(){
                     public void run() {
                         try{
                             gate.await();
                         }catch(Exception e){
                             throw new RuntimeException(e);
                         }
                         int array[] = new int[arrSize];
                         //arrSize * 10 took very long to run so made it
                         // just arrSize.
                         for (long i = 0; i < arrSize; i++) {
                             array[(int) ((i * i) % arrSize)]++;
                         }//for
                         long sum = 0;
                         for (int i = 0; i < arrSize; i++){
                              sum += array[i]; 
                         }
                         if(new Object().hashCode()==sum){
                              System.out.println("oh");
                         }//if
                         latch.countDown();
                      }//run
                   };//test
                exec.execute(test);
             }//for
             gate.await();
             long start = System.nanoTime();
             latch.await();
             long finish = System.nanoTime();
             System.out.println("Test with threadNum " +
                 threadNum +" took " + (finish-start) + " ns ");
             exec.shutdown();
             exec.awaitTermination(Long.MAX_VALUE,TimeUnit.SECONDS);           
        }//for
    }//main

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