Вид пула потоков со сбалансированной нагрузкой в ​​Java

Я ищу пул потоков со сбалансированной нагрузкой, но пока безуспешно. (Не уверен, что балансировка нагрузки правильная формулировка). Позвольте мне объяснить, чего я пытаюсь достичь.

Часть 1: У меня есть вакансии, от 8 до 10 отдельных задач. На 6-ядерном процессоре я позволил 8 потокам работать над этими задачами параллельно, что, кажется, обеспечивает лучшую производительность. Когда одна задача готова, можно начинать другую. Как только все десять задач выполнены, вся работа сделана. Обычно работа выполняется за 30-60 секунд.

Часть вторая: Иногда, к сожалению, работа занимает более двух часов. Это верно из-за количества данных, которые необходимо вычислить. Плохо то, что никакое другое задание не может быть запущено во время работы job1 (при условии, что все потоки имеют одинаковую продолжительность), потому что оно использует все потоки.

Моя первая идея: иметь 12 потоков, позволять выполнять до трех задач одновременно. НО: это означает, что при наличии только 1 задания куш не полностью готов.

Я ищу решение, позволяющее использовать полную мощность ЦП для выполнения первой работы, когда другой работы нет. Но когда нужно запустить другое задание, пока выполняется одно другое, я хочу, чтобы мощность ЦП была выделена для обоих заданий. И когда появляется третье или четвертое задание, я хочу, чтобы мощность процессора распределялась справедливо для всех четырех заданий.

Я ценю твои ответы ...

заранее спасибо


person Christian Rockrohr    schedule 19.01.2013    source источник
comment
Одна из возможностей может заключаться в масштабировании числа рабочих мест в соответствии с числом заданий, и позволить ОС обрабатывать квантование времени, когда рабочих больше, чем ядер.   -  person NPE    schedule 19.01.2013
comment
Хммм не уверен, понял ли я. вы имеете в виду, что у вас нет отдельных потоков для задач? Никогда не знаю, сколько рабочих мест будет параллельно. обычно есть только один, но в пиках их может быть 100. Вот почему мне нужно установить максимум 5 заданий. Потому что одновременно выполняется только одно задание, я хочу быть максимально быстрым и выполнять задачи задания с как можно большим количеством потоков (но только когда нет других заданий с их отдельными задачами)   -  person Christian Rockrohr    schedule 19.01.2013
comment
У вас может быть только три потока для трех заданий. Пусть каждый поток jOb может порождать еще четыре потока при выполнении задач внутри одного задания.   -  person Kanagavelu Sugumar    schedule 19.01.2013
comment
Канага ... это то, что у меня сейчас есть. В очереди на блокировку заданий просматривается 5 потоков. Когда когда-либо один поток получает новое задание, он запускает до четырех параллельных рабочих процессов для работы над отдельными задачами этого объекта ob. Когда другой поток задания просматривает другое задание, он делает то же самое. Таким образом, существуют параллельные потоки, когда одновременно выполняются две задачи, что абсолютно идеально. НО, когда одновременно выполняется только одно задание, оно не потребляет полную мощность процессора. по крайней мере два ядра не задействованы из-за всего четырех потоков.   -  person Christian Rockrohr    schedule 19.01.2013
comment
можете ли вы оценить время, чтобы закончить работу?   -  person Ralf H    schedule 19.01.2013
comment
Я могу только оценить нормальную работу. Обычно они выполняются от 30 до 60 секунд (для клиентов готовятся данные за три месяца). Но есть этот исключительный случай, когда задание должно рассчитывать данные за более чем 20 лет, а иногда и для нескольких клиентов одновременно. Эта работа длится до четырех часов (в будущем время увеличится, поскольку объем данных растет с каждым днем)   -  person Christian Rockrohr    schedule 19.01.2013


Ответы (3)


Одна из возможностей - использовать стандартный ThreadPoolExecutor с другим типом очереди задач.

public class TaskRunner {
  private static class PriorityRunnable implements Runnable,
            Comparable<PriorityRunnable> {
    private Runnable theRunnable;
    private int priority = 0;
    public PriorityRunnable(Runnable r, int priority) {
      this.theRunnable = r;
      this.priority = priority;
    }

    public int getPriority() {
      return priority;
    }

    public void run() {
      theRunnable.run();
    }

    public int compareTo(PriorityRunnable that) {
      return this.priority - that.priority;
    }
  }

  private BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<Runnable>();

  private ThreadPoolExecutor exec = new ThreadPoolExecutor(8, 8, 0L,
            TimeUnit.MILLISECONDS, taskQueue);

  public void runTasks(Runnable... tasks) {
    int priority = 0;
    Runnable nextTask = taskQueue.peek();
    if(nextTask instanceof PriorityRunnable) {
      priority = ((PriorityRunnable)nextTask).getPriority() + 1;
    }
    for(Runnable t : tasks) {
      exec.execute(new PriorityRunnable(t, priority));
      priority += 100;
    }
  }
}

Идея здесь в том, что когда у вас появляется новая работа, вы звоните

taskRunner.runTasks(jobTask1, jobTask2, jobTask3);

и он будет ставить задачи в очередь таким образом, чтобы они хорошо чередовались с любыми существующими задачами в очереди (если таковые имеются). Предположим, у вас есть одно задание в очереди, задачи которого имеют номера приоритета j 1 t 1 = 3, j 1 t 2 = 103 и j 1 t 3 = 203. При отсутствии других заданий эти задачи будут выполняться одна за другой как можно быстрее. Но если вы отправите другое задание с тремя собственными задачами, им будут назначены номера приоритета j 2 t 1 = 4, j 2 t 2 = 104 и j 2 t 3 = 204, что означает, что очередь теперь выглядит как

j 1 t 1, j 2 t 1, j 1 t 2, j 2 t 2 и т. Д.

Однако это не идеально, потому что, если все потоки в настоящее время работают (на задачах из задания 1), то первая задача задания 2 не может быть запущена, пока одна из задач задания 1 не будет завершена (если нет какого-либо внешнего способа обнаружения это и прерывает и повторно ставит в очередь некоторые задачи задания 1). Самый простой способ сделать ситуацию более справедливой - это разбить более длительные задачи на более мелкие сегменты и поставить их в очередь как отдельные задачи - вам нужно добраться до точки, когда каждое отдельное задание включает в себя больше задач, чем потоков в пуле, так что некоторые задачи всегда будут запускаться в очереди, а не назначаться непосредственно потокам (если есть незанятые потоки, exec.execute() передает задачу прямо потоку, не проходя через очередь вообще).

person Ian Roberts    schedule 19.01.2013
comment
Я тоже думал об этой идее. Но это не поможет (как вы заметили), когда задание использует весь пул потоков. Всем предстоящим задачам придется подождать, пока не будет готова эта большая. Если бы я знал заранее, что задание большое, я бы мог уменьшить количество рабочих потоков, чтобы не блокировать выполнение обычных заданий. Возможно, я смогу расширить свой интерфейс связи до as400.jobqueue, чтобы также сообщить мне количество месяцев, которые покрываются этой работой. (Будет работать, но это еще не оптимальное решение, которое я ищу :-)) спасибо ... - person Christian Rockrohr; 19.01.2013
comment
наконец я выбрал приоритетную идею. Не совсем так, как вы описали. Подробности в комментариях ко второму ответу. - person Christian Rockrohr; 20.01.2013

Как предлагает Канага, проще всего сделать так, чтобы у вашего процессора было слишком много ресурсов, но запускать по 8 потоков каждый. У конкурентов могут быть некоторые накладные расходы, но если вы дойдете до ситуации с одним заданием, он полностью загрузит ЦП. ОС будет выделять время каждому потоку.

Ваша «первая идея» тоже подойдет. Простаивающие потоки не будут забирать ресурсы у 8 рабочих потоков, если они на самом деле не выполняют задачу. Однако это не распределяет ресурсы процессора так же равномерно, когда выполняется несколько заданий.

У вас есть установка, где вы можете протестировать эти разные конвейеры, чтобы увидеть, как они работают для вас?

person Joshua Martell    schedule 19.01.2013
comment
Да, запуск 8 потоков для каждого задания пока работает очень хорошо. Но, как упоминалось выше, это не круто :-) Когда действительно одновременно выполняется три задания, будет выполняться 24 потока. Не блокирующий, но и не оптимальный. Переключение контекста снизило бы мою производительность. (Я знаю, что это всего лишь кусок каке по сравнению с остальной работой, но я ищу что-то действительно впечатляющее и полностью подходящее для моей проблемы. - person Christian Rockrohr; 19.01.2013
comment
Да, у меня есть полная тестовая среда. Я проведу какой-нибудь модульный тест, чтобы измерить производительность. Но сначала я хотел бы собрать еще несколько идей. - person Christian Rockrohr; 19.01.2013
comment
Если вы используете 8 потоков на задание, я бы ограничил его двумя одновременными заданиями. Вы должны суметь оценить, какой должна быть идеальная производительность потока, и сравнить ее с вашими измеренными результатами. - person Joshua Martell; 20.01.2013
comment
Спасибо за ваш отзыв. Я реализовал функцию, чтобы оценить, обычная это работа или большая работа. Когда он нормальный, он использует 8 потоков. если он ненормальный, он не должен использовать более 3 потоков. Вдобавок я ограничил количество параллельных заданий двумя. Это не то, что я искал изначально, но он вполне соответствует моим потребностям. Теперь я проведу несколько тестов и измерю производительность, чтобы точно настроить количество потоков. - person Christian Rockrohr; 20.01.2013

Думаю, раз уж ваша машина - это 6-ядерный процессор. Лучше иметь 6 рабочих потоков для каждого потока задания. Таким образом, когда каждый поток получает новое задание, он запускает до шести параллельных рабочих процессов для работы над одним заданием. Это обеспечит использование всей мощности процессора при одновременном выполнении только одного задания.

Также ознакомьтесь с концепцией Fork and Join в java 7.
Ссылки_1
Ссылки_2
Ссылки_3
References_4

Также узнайте о newcachedthreadpool ()

Java newCachedThreadPool () по сравнению с newFixedThreadPool

person Kanagavelu Sugumar    schedule 19.01.2013