Как масштабировать потоки в соответствии с ядрами процессора?

107

Я хочу решить математическую задачу с несколькими потоками в Java. мою математическую задачу можно разделить на рабочие единицы, которые я хочу решить в несколько потоков.

Я хочу, чтобы над ним работало не фиксированное количество потоков, а количество потоков, соответствующее количеству ядер ЦП. Моя проблема в том, что я не смог найти в Интернете простой учебник по этому поводу. Все, что я нашел, - это примеры с фиксированными потоками.

Как это может быть сделано? Вы можете привести примеры?

Андреас Хорниг
источник

Ответы:

120

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

Обновление : чтобы уточнить, поток - это просто объект в Java, поэтому вы можете создать его так же, как и любой другой объект. Итак, допустим, вы вызываете вышеуказанный метод и обнаруживаете, что он возвращает 2 процессора. Потрясающие. Теперь вы можете создать цикл, который генерирует новый поток, разделяет работу для этого потока и запускает поток. Вот какой-то псевдокод, чтобы продемонстрировать, что я имею в виду:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

Для получения дополнительной информации о создании собственной темы перейдите к этому руководству . Кроме того, вы можете посмотреть пул потоков для создания потоков.

JasCav
источник
17
Это в основном правильно, но будьте осторожны с производительностью процессоров, продаваемых с «гиперпоточностью» Intel. На четырехъядерном процессоре это вернет 8 вместо 4, но ваша производительность может начать падать после 4 потоков - так что мои собственные тесты говорят мне :)
xcut
Привет, хорошо, не знала, что такое возможно. но когда я разделяю одну задачу на несколько рабочих блоков и мне нужно решение по частям для заключительного рабочего этапа, как это делается? Когда у меня есть несколько "yourThreads", как мне использовать для этого join (), потому что я не вижу, как эти несколько потоков различимы? :) Кстати: ваша ссылка на пул потоков привела меня к ibm.com/developerworks/library/j-jtp0730.html :)
Андреас Хорниг,
5
Посмотрите на пример здесь: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Он расскажет вам о более оптимизированном способе создания пула потоков и управления им ... Может показаться сначала сложнее, но, как и в большинстве случаев, это еще сложнее, потому что, если бы было проще, вы бы раньше столкнулись с ограничениями.
Bill K
62

Вы, вероятно, захотите также посмотреть на фреймворк java.util.concurrent для этого. Что-то вроде:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

или

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

Это намного лучше, чем иметь дело с собственными пулами потоков и т. Д.

Дэйв
источник
Привет, DaveC, хммм, не знал этого раньше, так что я посмотрю на это. А его можно масштабировать по имеющимся ядрам процессора? Потому что я не вижу этого в ваших коротких примерах. С уважением, Андреас
Андреас Хорниг
3
java.util.concurrent обладает высокой масштабируемостью
Кристофер Айвс,
4
Пул фиксированного размера с количеством доступных процессоров часто является оптимальным для процессов, связанных с процессором. Все, что вам нужно сделать, - это первый пример.
Питер Лоури
1
Как указано в первом комментарии к принятому ответу, было бы лучше использовать половину количества сообщаемых «процессоров» по ​​двум причинам: 1. если у вас есть гиперпоточность, реальное количество процессоров составляет половину того, что сообщается. , и 2. это дает некоторую вычислительную мощность для работы остальной системы (ОС и других программ).
Matthieu
10

Опция 1:

newWorkStealingPool изExecutors

public static ExecutorService newWorkStealingPool()

Создает пул потоков, требующих трудозатрат, используя все доступные процессоры в качестве целевого уровня параллелизма.

С этим API вам не нужно передавать количество ядер ExecutorService.

Реализация этого API из grepcode

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

Вариант 2:

newFixedThreadPool API из Executorsили other newXXX constructors, который возвращаетExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

замените nThreads на Runtime.getRuntime().availableProcessors()

Вариант 3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

передать Runtime.getRuntime().availableProcessors()как параметр в maximumPoolSize.

Равиндра бабу
источник
8

У Дуга Ли (автора параллельного пакета) есть эта статья, которая может иметь отношение к делу: http://gee.cs.oswego.edu/dl/papers/fj.pdf

Фреймворк Fork Join был добавлен в Java SE 7. Ниже приведены еще несколько ссылок:

http://www.ibm.com/developerworks/java/library/j-jtp11137/index.html Статья Брайана Гетца

http://www.oracle.com/technetwork/articles/java/fork-join-422606.html

Давид Сороко
источник
4

Стандартный способ - это метод Runtime.getRuntime (). AvailableProcessors (). На большинстве стандартных процессоров вы вернете здесь оптимальное количество потоков (которое не является фактическим количеством ядер процессора). Следовательно, это то, что вы ищете.

Пример:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

НЕ забудьте закрыть службу-исполнитель следующим образом (иначе ваша программа не выйдет):

service.shutdown();

Вот лишь краткое описание того, как настроить будущий MT-код (оффтоп, для иллюстрации):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

Затем вам нужно отслеживать, сколько результатов вы ожидаете, и получать их следующим образом:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}
fl0w
источник
2
вызов выключения должен быть введен в действие, наконец,
Кристоф Русси
1
@ChristopheRoussy, вы совершенно правы, я соответствующим образом изменил фрагмент, спасибо!
fl0w 05
3

В классе Runtime есть метод с именем availableProcessors (). Вы можете использовать это, чтобы выяснить, сколько у вас процессоров. Поскольку ваша программа привязана к ЦП, вы, вероятно, захотите иметь (максимум) один поток на доступный ЦП.

Эрик Петрелье
источник
Привет, Джейсон и Эрик (я использую один комментарий для обоих ваших ответов, потому что в основном это одно и то же). хорошо, это неплохо проверить, но это будет первая часть. Когда у меня есть количество ядер, мне нужно, чтобы потоки были такими же переменными, как это количество ядер. Я пробовал этот пример до openbook.galileodesign.de/javainsel5/… (немецкий!), И он использует фиксированный поток. Но я хочу иметь такое же программирование, используя 2 ядра в двухъядерной среде и 4 ядра в четырехъядерной среде. Я не хочу менять его вручную. Это возможно? СПАСИБО! :)
Андреас Хорниг
@Andreas - Смотрите обновления, которые я внес в свой пост. Думаю, это поможет прояснить вопрос.
JasCav