2011-08-01 2 views
1

Здесь я хотел бы сосредоточиться на пользовательском приложении, где я получил деградацию (нет необходимости в общем обсуждении устойчивости потоков к процессам).java threads vs java процессы деградации производительности

У меня есть приложение MPI на Java, которое решает некоторые проблемы с помощью метода итерации. Схематический вид прикладного сильфона позволяет называть его MyProcess (п), где «п» является число процессов:

double[] myArray = new double[M*K]; 

for(int iter = 0;iter<iterationCount;++iter) 
{ 
    //some communication between processes 

    //main loop 
    for(M) 
    for(K) 
    { 
     //linear sequence of arithmetical instructions 
    } 

    //some communication between processes 
} 

Для повышения производительности я решил использовать Java тему (позволяет называть его MyThreads (п)). Код почти тот же: myArray становится матрицей, где каждая строка содержит массив для соответствующего потока.

double[][] myArray = new double[threadNumber][M*K]; 


public void run() 
{ 
    for(int iter = 0;iter<iterationCount;++iter) 
    { 
    //some synchronization primitives 

    //main loop 
    for(M) 
     for(K) 
     { 
      //linear sequence of arithmetical instructions 

      counter++; 
     } 

    // some synchronization primitives 
    } 
} 

Темы создавались и запускались с использованием Executors.newFixedThreadPool (threadNumber).

Проблема в том, что в то время как для MyProcess (n) мы получили адекватную производительность (n в [1,8]), в случае производительности MyThreads (n) существенно ухудшается (в моей системе в n раз).

Оборудование: Intel (R) Xeon (R) CPU X5355 (2 процессора, 4 ядер на каждом)

Java Версия: 1.5 (с использованием опции D32).

Сначала я думал, что получил разные рабочие нагрузки на потоки, но нет, переменный «счетчик» показывает, что количество итераций между разными запусками MyThreads (n) (n в [1,8]) идентично.

И это не ошибка синхронизации, потому что у меня есть временный комментарий для всех примитивов синхронизации.

Любые предложения/идеи будут оценены.

Спасибо.

+0

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

+0

Спасибо за ответ. Ну, после комментирования примитивов синхронизации оставшийся код является независимым для всех потоков. Это означает, что в этот момент каждый поток просто вычисляет его результаты и не связывается. Это приведет к неправильной матрице результатов, но по причинам отладки не заботятся. – manuk

+0

Можете ли вы разместить некоторое примерное значение M, K и iterationCount? – user802421

ответ

0

Есть 2 вопроса, которые я вижу в вашем фрагменте кода.


Во-первых, проблема с кешированием. Поскольку вы пытаетесь сделать это в нескольких потоках/процессах, я бы предположил, что ваши результаты M * K имеют большое количество; то когда вы делаете

double[][] myArray = new double[threadNumber][M*K]; 

Вы по существу создаете массив двойных указателей с размером threadNumber; каждый из которых указывает на двойной массив размера M * K. Интересным здесь является то, что количество массивов threadNumber не обязательно распределяется на один и тот же блок памяти. Это просто двойные указатели, которые могут быть размещены где угодно внутри кучи JVM. В результате при запуске нескольких потоков вы можете столкнуться с большим количеством промахов в кеше, и вы в конечном итоге читаете память много раз, в конечном итоге замедляете свою программу.

Если выше, является основной причиной, вы можете попробовать увеличить размер виртуальной машины Java кучи, а затем сделать

double[] myArray = new double[threadNumber * M * K]; 

И есть нити, работающие на различных сегментах одного и того же массива. Вы должны уметь лучше видеть производительность.


Во второй раз проблема синхронизации. Обратите внимание, что двойной (или любой примитивный) массив НЕ изменчив. Таким образом, ваш результат в 1 потоке не гарантированно будет отображаться для других потоков.Если вы используете блок синхронизации, это устраняет проблему, так как побочным эффектом синхронизации является обеспечение видимости по потокам; Если нет, когда вы читаете и записываете массив, всегда убедитесь, что вы используете Unsafe.putXXXVolatile() и Unsafe.getXXXVolatile(), чтобы вы могли выполнять изменчивые операции над массивами.

Чтобы сделать это, Unsafe также может использоваться для создания непрерывного сегмента памяти, который вы можете использовать для хранения вашей структуры данных и достижения лучшей производительности. В вашем случае я думаю, что 1) уже делает трюк.