İşlemciler CPU çekirdeklerine göre nasıl ölçeklenir?


107

Java'da birden çok iş parçacığı olan matematiksel bir problemi çözmek istiyorum. matematik problemim birkaç iş parçacığında çözmek istediğim çalışma birimlerine ayrılabilir.

Üzerinde çalışan sabit miktarda iş parçacığı olmasını istemiyorum, bunun yerine CPU çekirdeği miktarıyla eşleşen bir iş parçacığı miktarı istiyorum. Benim sorunum, internette bunun için kolay bir eğitim bulamamam. Bulduğum tek şey sabit iş parçacıklı örnekler.

Bu nasıl yapılabilir? Örnek verebilir misiniz?

Yanıtlar:


120

Sen, statik Süre yöntemini kullanarak Java Virtual Machine mevcut süreçlerin sayısını belirleyebilir availableProcessors . Mevcut işlemci sayısını belirledikten sonra, bu sayıda iş parçacığı oluşturun ve çalışmanızı buna göre bölün.

Güncelleme : Daha fazla açıklığa kavuşturmak gerekirse, bir İş Parçacığı yalnızca Java'da bir Nesnedir, bu nedenle herhangi bir nesneyi oluşturduğunuz gibi onu oluşturabilirsiniz. Öyleyse yukarıdaki yöntemi çağırdığınızı ve 2 işlemci döndürdüğünü görelim. Harika. Şimdi, yeni bir İş Parçacığı oluşturan ve bu iş parçacığı için çalışmayı bölen ve iş parçacığını ateşleyen bir döngü oluşturabilirsiniz. İşte ne demek istediğimi göstermek için bazı psuedocode:

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

Kendi ileti dizinizi oluşturma hakkında daha fazla bilgi için bu eğiticiye gidin . Ayrıca, iş parçacığı oluşturmak için İş Parçacığı Havuzuna bakmak isteyebilirsiniz .


17
Bu temelde doğrudur, ancak Intel'in "hiper iş parçacığı" ile pazarlanan işlemcilerdeki performans konusunda dikkatli olun. Bir dört çekirdekte, bu 4 yerine 8 döndürür, ancak performansınız aslında 4 iş parçacığından sonra
düşmeye

Merhaba, tamam, bunun mümkün olduğunu bilmiyordum. ancak bir görevi birkaç iş birimine böldüğümde ve son iş adımı için tüm parça çözüme ihtiyacım olduğunda, bu nasıl yapılır? Birkaç "yourThreads" bende olduğunda bunun için join () 'yi nasıl kullanırım, çünkü görmüyorum, bu birkaç iş parçacığı nasıl ayırt edilebilir? :) BTW: Konu Havuzuna bağlantınız beni ibm.com/developerworks/library/j-jtp0730.html adresine yönlendiriyor :)
Andreas Hornig

5
Buradaki örneğe bakın: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Size iş parçacığı havuzunu oluşturmanın ve yönetmenin daha akıcı bir yolunu anlatacak ... ilk başta daha karmaşık, ancak çoğu şeyde olduğu gibi, daha karmaşık çünkü daha basit olsaydı, sınırlamalara daha çabuk ulaşırdın.
Bill K

62

Muhtemelen bu şeyler için java.util.concurrent çerçevesine de bakmak istersiniz. Gibi bir şey:

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

veya

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

Bu, kendi iş parçacığı havuzlarınızla başa çıkmaktan çok daha hoş.


Merhaba DaveC, hmmm, bunu daha önce bilmiyordum, bu yüzden buna bir göz atacağım. Ve mevcut cpu çekirdeklerine göre ölçeklendirilebilir mi? Çünkü siz kısa örneklerde bunu göremiyorum. Saygılarımızla, Andreas
Andreas Hornig

3
java.util.concurrent oldukça ölçeklenebilir
Kristopher Ives

4
Kullanılabilir işlemci sayısıyla sabit boyutlu bir havuz, genellikle CPU'ya bağlı işlemler için idealdir. Buradaki ilk örnek, yapmanız gereken tek şey.
Peter Lawrey

1
Kabul edilen cevabın ilk yorumunda belirtildiği gibi, iki nedenden ötürü bildirilen "İşlemci" sayısının yarısını kullanmak daha iyi olacaktır: 1. hiper iş parçacığınız varsa, gerçek işlemci sayısı bildirilenin yarısıdır ve 2. Sistemin geri kalanı için bir miktar işlem gücünün çalışmasına izin verir (OS ve diğer programlar).
Matthieu

10

Seçenek 1:

newWorkStealingPool danExecutors

public static ExecutorService newWorkStealingPool()

Hedef paralellik düzeyi olarak tüm kullanılabilir işlemcileri kullanarak bir iş hırsızlığı iş parçacığı havuzu oluşturur.

Bu API ile çekirdek sayısını aktarmanıza gerek yoktur ExecutorService.

Bu API'nin grepcode'dan uygulanması

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

Seçenek 2:

newFixedThreadPool API'si Executorsveya other newXXX constructorsdöndürürExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

nThreads değiştir Runtime.getRuntime().availableProcessors()

3. Seçenek:

ThreadPoolExecutor

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

geçmesi Runtime.getRuntime().availableProcessors()için parametre olarak maximumPoolSize.



4

Standart yol, Runtime.getRuntime (). AvailableProcessors () yöntemidir. Çoğu standart CPU'da optimum iş parçacığı sayısını (bu gerçek CPU çekirdek sayısı değildir) burada döndürmüş olacaksınız. Bu nedenle aradığınız şey bu.

Misal:

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

Yürütme hizmetini bu şekilde kapatmayı UNUTMAYIN (aksi takdirde programınız çıkmaz):

service.shutdown();

Geleceğe dayalı bir MT kodunun nasıl kurulacağına dair kısa bir özet burada (örnek olarak konu dışı):

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

O halde, kaç sonuç beklediğinizi takip etmeniz ve bunları aşağıdaki gibi almanız gerekir:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}

2
kapatma çağrısı nihayet denenmeli
Christophe Roussy

1
@ChristopheRoussy Çok haklısınız, pasajı buna göre değiştirdim, teşekkürler!
fl0w

3

Runtime sınıfında, availableProcessors () adında bir yöntem vardır. Bunu kaç CPU'nuz olduğunu bulmak için kullanabilirsiniz. Programınız CPU'ya bağlı olduğundan, mevcut CPU başına (en fazla) bir iş parçacığına sahip olmak isteyebilirsiniz.


Merhaba Jason ve Eric (Her iki cevabınız için bir yorum kullanıyorum çünkü temelde aynı). tamam, kontrol etmek güzel, ama bu ilk bölüm olacak. Çekirdek sayısına sahip olduğumda, bu çekirdek miktarı kadar değişken olarak iş parçacığına sahip olmam gerekiyor. Bu örneği openbook.galileodesign.de/javainsel5/… (Almanca!) ' Dan önce denedim ve sabit bir iş parçacığı kullanıyor. Ama aynı programlamayı çift çekirdekli bir ortamda 2 çekirdek ve dört çekirdekli bir ortamda 4 çekirdek kullanarak yapmak istiyorum. Manuel olarak değiştirmek istemiyorum. Mümkün mü? TEŞEKKÜRLER! :)
Andreas Hornig

@Andreas - Gönderimde yaptığım güncellemeleri görün. Bunun sorunu netleştirmeye yardımcı olacağını düşünüyorum.
JasCav
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.