Java-потоки против снижения производительности Java-процессов - PullRequest
1 голос
/ 01 августа 2011

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

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

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 (n)). Код почти такой же - 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).

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

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

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

Спасибо.

1 Ответ

0 голосов
/ 27 августа 2013

В вашем куске кода есть 2 проблемы.


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

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

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

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

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

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


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

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

...