2015-06-28 6 views
1

Я работаю над бенчмаркингом, созданным с помощью Java. Я запускаю этот код дважды в главном потоке, а затем один раз в два отдельных нитей:Почему два потока вычисляются медленнее, чем один?

if (testing == null) { 
    testing = new byte[TEST_SIZE][TEST_SIZE][TEST_SIZE]; 
} 

for (int x = 0; x < TEST_SIZE; x ++) { 
    for (int y = 0; y < TEST_SIZE; y ++) { 
     for (int z = 0; z < TEST_SIZE; z ++) { 
      testing[x][y][z] = (byte)RANDOM.nextInt(100); 
     } 
    } 
} 

if (finished == Test.LOOP_COUNT - 1) { 
    testing = null; 
} 

Задача на основной поток завершается гораздо быстрее, чем две нити, как показано на выходе приложения:

Starting test Array Handling with a single core. 
Loop #1 finished in 1.820588011 seconds. 
Loop #2 finished in 1.779667175 seconds. 
Finished in 3 seconds. 
Starting test Array Handling with multiple cores. 
Loop #2 finished in 9.433253526 seconds. 
Loop #1 finished in 9.465652985 seconds. 
Finished in 9 seconds. 

Я прочитал кое-что где-то, в котором указано, что два потока, выполняющие действительно быстрые операции, не будут работать так же, как и один поток, но эти два потока, работающие на более сложных операциях, превосходят один поток. Я не думал, что это так, потому что каждый цикл довольно требовательный. Единственная причина, по которой я могу думать, - нить на самом деле не работает на собственных ядрах. Это может быть проблема? У меня 2-ядерный 4-процессорный Intel Core i7-3537U.

EDIT:

испытаний Класс:

package net.jibini.park.tests; 

import java.util.Random; 
import java.util.concurrent.CopyOnWriteArrayList; 

/** 
* 
* @author zgoethel12 
*/ 
public abstract class Test { 

public static final Random RANDOM = new Random(); 
public static final int LOOP_COUNT = 2; 

public static final CopyOnWriteArrayList<Test> tests = new CopyOnWriteArrayList<Test>(); 

public int finished = 0; 
public int longestTime = 0; 
public double timeSum = 0; 

static { 
    RANDOM.setSeed(481923); 
    tests.add(new TestArray()); 
} 

public abstract String getName(); 

public void runTest(final boolean multithread) { 

    new Thread(new Runnable() { 
      @Override 
      public void run() { 
       finished = 0; 
       longestTime = 0; 
       timeSum = 0; 

       if (multithread) { 
        for (int i = 0; i < LOOP_COUNT; i ++) { 
         final int f = i; 
         new Thread(new Runnable() { 
          @Override 
          public void run() { 
           doLoop(f + 1); 
           Thread.currentThread().interrupt(); 
          } 
         }).start(); 
        } 
       } else { 
        for (int i = 0; i < LOOP_COUNT; i ++) { 
         doLoop(i + 1); 
        } 
       } 

       while (finished < LOOP_COUNT) { 
        System.out.print(""); 
       } 

       System.out.println("Finished in " + (multithread ? longestTime : (int)timeSum) + " seconds."); 
       Thread.currentThread().interrupt(); 
      } 
    }).start(); 

} 

public void doLoop(int id) { 

    long start = System.nanoTime(); 
    doTest(id); 
    handleLoopFinish(id, start); 

} 

public abstract void doTest(int id); 

public void handleLoopFinish(int id, long start) { 

    long current = System.nanoTime(); 
    long difference = current - start; 
    double seconds = (double)difference/1000000000; 
    if (seconds > longestTime) { 
     longestTime = (int)seconds; 
    } 
    timeSum += seconds; 
    System.out.println("Loop #" + id + " finished in " + seconds + " seconds."); 
    finished ++; 

} 

} 

Массив Тест:

package net.jibini.park.tests; 

/** 
* 
* @author zgoethel12 
*/ 
public class TestArray extends Test { 

public static final int TEST_SIZE = 512; 

byte[][][] testing = null; 

@Override 
public void doTest(int id) { 

    if (testing == null) { 
     testing = new byte[TEST_SIZE][TEST_SIZE][TEST_SIZE]; 
    } 

    for (int x = 0; x < TEST_SIZE; x ++) { 
     for (int y = 0; y < TEST_SIZE; y ++) { 
      for (int z = 0; z < TEST_SIZE; z ++) { 
       testing[x][y][z] = (byte)RANDOM.nextInt(100); 
      } 
     } 
    } 

    if (finished == Test.LOOP_COUNT - 1) { 
     testing = null; 
    } 

} 

@Override 
public String getName() { 
    return "Array Handling"; 
} 

} 
+0

Показать свой многопоточный код. Если вы использовали то же самое, но активировали его в каждом потоке, то потоки должны были бороться за право писать на один и тот же объект в ОЗУ - это была задержка – Toumash

+1

, вполне вероятно, что ваш тест просто неверен, например. пренебрегая разминкой, попробуйте использовать [jmh] (http://openjdk.java.net/projects/code-tools/jmh/) и посмотрите, не получили ли вы все эти результаты. – the8472

+0

Как вы разделили работу между двумя потоками? –

ответ

2

Вы, кажется, используете только один объект RANDOM. Я боюсь, что это разделено между двумя потоками, что может сделать их очень медленными.

Попробуйте использовать ThreadLocalRandom.

+0

На самом деле, я работаю на Java 6, поэтому я просто создал новые экземпляры. –

+0

Вы должны действительно рассмотреть возможность обновления, особенно если вы хотите иметь лучшую производительность - многие небольшие изменения в внутренних компонентах JVM могут иметь огромное значение. – claj

0

Запуск второго потока, в зависимости от того, что библиотек, которые вы выбираете для выполнения многопоточности, принимает много накладных расходов, и операция, которую вы, кажется, делаете, в зависимости от того, насколько велика TEST_SIZE, вероятно, очень эффективно выполняется b y один CPU, потому что вы перемещаете массив в памяти смежным образом.

Мне было поручено в курсовой работе, что встроенная библиотека Java Thread имеет гораздо больше накладных расходов, чем в ForkJoin, поэтому, возможно, вы получите больше ожидаемых результатов с использованием этой библиотеки.

В качестве ресурса, вот хороший сайт, который я использовал в своем классе параллельности: http://homes.cs.washington.edu/~djg/teachingMaterials/spac/grossmanSPAC_forkJoinFramework.html

Будьте осторожны, чтобы сделать разогреть пробегов, как сказано ниже. Они играют решающую роль в обеспечении того, чтобы вы могли видеть преимущества. Мы попытались сделать ~ 100 прогонов, с ~ 10 как разогрев, чтобы убедиться, что вы получаете хорошие средние значения. Усреднение во многих прогонах помогает многому из-за переключения контекста/других компьютерных процессов, которые могут добавить изменчивость к вашим испытаниям!

+0

Благодарим вас за ответ. Мне нужно, чтобы потоки были частью бенчмаркинга, и значение TEST_SIZE слишком сильно приводило к исчерпанию памяти. Я выделил ему 6 ГБ. Я посмотрю в Форк-Джона. –

+0

Конечно! Удачи! –

+0

Проблема заключалась в использовании единственного экземпляра Random. Я исправил это, и время, необходимое для обработки, было примерно вдвое. –