Como escalar threads de acordo com os núcleos da CPU?

107

Quero resolver um problema matemático com vários threads em Java. meu problema de matemática pode ser separado em unidades de trabalho, que desejo resolver em vários tópicos.

Não quero ter uma quantidade fixa de threads trabalhando nele, mas sim uma quantidade de threads que corresponda à quantidade de núcleos da CPU. Meu problema é que não consegui encontrar um tutorial fácil na internet para isso. Tudo o que encontrei são exemplos com threads fixos.

Como isso pode ser feito? Você pode fornecer exemplos?

Andreas Hornig
fonte

Respostas:

119

Você pode determinar o número de processos disponíveis para a Java Virtual Machine usando o método estático Runtime, availableProcessors . Depois de determinar o número de processadores disponíveis, crie esse número de threads e divida seu trabalho de acordo.

Atualização : Para esclarecer ainda mais, um Thread é apenas um Objeto em Java, então você pode criá-lo como faria com qualquer outro objeto. Então, digamos que você chame o método acima e descubra que ele retorna 2 processadores. Impressionante. Agora, você pode criar um loop que gera um novo Thread, divide o trabalho para aquele thread e dispara o thread. Aqui estão alguns psuedocódigo para demonstrar o que quero dizer:

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();
}

Para obter mais informações sobre como criar seu próprio tópico, vá para este tutorial . Além disso, você pode querer dar uma olhada em Thread Pooling para a criação de threads.

JasCav
fonte
17
Basicamente, isso está correto, mas tenha cuidado com o desempenho dos processadores comercializados com o "hyper-threading" da Intel. Em um quad-core, isso retornará 8 em vez de 4, mas seu desempenho pode realmente começar a cair após 4 threads - meus próprios benchmarks me dizem :)
xcut
Olá, ok, não sabia, que isso é possível. mas quando divido uma tarefa em várias unidades de trabalho e preciso de todas as partes da solução para a etapa de trabalho final, como isso é feito? Quando tenho vários "yourThreads", como uso join () para isso, porque não vejo como esses vários threads são distinguíveis? :) A propósito: seu link para Thread Pooling me leva a ibm.com/developerworks/library/j-jtp0730.html :)
Andreas Hornig
5
Veja o exemplo aqui: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Ele mostrará uma maneira mais simplificada de criar e gerenciar o pool de threads ... Pode parecer mais complicado no início, mas como a maioria das coisas, é mais complicado porque se fosse mais simples você acertaria as limitações mais cedo.
Bill K
62

Você provavelmente deseja examinar a estrutura java.util.concurrent para essas coisas também. Algo como:

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

ou

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

Isso é muito mais agradável do que lidar com seus próprios pools de threads, etc.

DaveC
fonte
Olá DaveC, hmmm, não sabia disso antes, então vou dar uma olhada nisso. E pode ser escalado de acordo com os núcleos de CPU disponíveis? Porque não consigo ver isso em seus exemplos curtos. Atenciosamente, Andreas
Andreas Hornig
3
java.util.concurrent é altamente escalável
Kristopher Ives
4
Um pool de tamanho fixo com o número de processadores disponíveis geralmente é ideal para processos vinculados à CPU. O primeiro exemplo aqui é tudo que você precisa fazer.
Peter Lawrey
1
Conforme afirmado no primeiro comentário da resposta aceita, seria melhor usar metade do número de "Processadores" relatados, por dois motivos: 1. se você tiver hyper-threading, o número real de processadores é metade do que é relatado e 2. permite algum poder de processamento para o resto do sistema funcionar (SO e outros programas).
Matthieu
10

Opção 1:

newWorkStealingPool deExecutors

public static ExecutorService newWorkStealingPool()

Cria um pool de threads de roubo de trabalho usando todos os processadores disponíveis como seu nível de paralelismo de destino.

Com esta API, você não precisa passar o número de núcleos para ExecutorService.

Implementação desta API de 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);
    }

Opção 2:

API newFixedThreadPool de Executorsou other newXXX constructors, que retornaExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

substituir nThreads por Runtime.getRuntime().availableProcessors()

Opção 3:

ThreadPoolExecutor

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

passe Runtime.getRuntime().availableProcessors()como parâmetro para maximumPoolSize.

Ravindra babu
fonte
4

A forma padrão é o método Runtime.getRuntime (). AvailableProcessors (). Na maioria das CPUs padrão, você retornou a contagem ideal de threads (que não é a contagem real do núcleo da CPU) aqui. Portanto, é isso que você está procurando.

Exemplo:

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

NÃO se esqueça de encerrar o serviço do executor desta forma (ou seu programa não sairá):

service.shutdown();

Aqui está apenas um esboço rápido de como configurar um código MT baseado no futuro (offtopic, para ilustração):

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)));
    }

Em seguida, você precisa acompanhar quantos resultados espera e recuperá-los assim:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}
fl0w
fonte
2
chamada de desligamento deve ser colocada em tentativa finalmente
Christophe Roussy
1
@ChristopheRoussy você está certo, modifiquei o snippet de acordo, obrigado!
fl0w
3

Na classe Runtime, existe um método chamado availableProcessors (). Você pode usar isso para descobrir quantas CPUs você tem. Como seu programa é limitado pela CPU, você provavelmente desejaria ter (no máximo) um thread por CPU disponível.

Eric Petroelje
fonte
Olá Jason e Eric (utilizo um comentário para ambas as respostas, porque é basicamente o mesmo). ok, é bom verificar, mas esta seria a primeira parte. Quando tenho a contagem de núcleos, tenho que ter threads tão variáveis ​​quanto essa quantidade de núcleos. Eu tentei este exemplo antes de openbook.galileodesign.de/javainsel5/… (Alemão!) E ele usa um thread fixo. Mas eu quero ter a mesma programação usando 2 núcleos em um ambiente dual-core e 4 núcleos em um ambiente quad-core. Não quero alterá-lo manualmente. Isso é possível? THX! :)
Andreas Hornig
@Andreas - Veja as atualizações que fiz na minha postagem. Acho que isso ajudará a esclarecer o problema.
JasCav