簡體   English   中英

100%CPU使用率的Java線程優化

[英]Java threading optimization at 100% CPU usage

我有一個應用程序接受隊列上的工作,然后旋轉,在獨立的線程上完成。 線程數量不大,比如高達100,但這些是密集型任務,可以快速將CPU提升到100%。

為了最快地完成大部分工作:我最好在我需要做更多工作時啟動更多線程,讓Java線程調度程序處理分配工作,或者更聰明地管理工作負載以保持CPU低於100%讓我更快?

這台機器專用於我的java應用程序。

編輯:

感謝您的精彩輸入!

這些任務具有不同的復雜性並且涉及I / O,因此具有較低的4個線程池可能只是將CPU運行到20%。 我無法知道有多少任務實際上將CPU帶到100%。

我的想法是我應該通過RMI監控CPU並動態撥打上下工作,或者我應該不關心並讓操作系統處理它。

如果在並行線程中有太多同時執行計算密集型任務,則會很快達到收益遞減的程度。 實際上,如果有N個處理器(核心),那么你不需要超過N個這樣的線程。 現在,如果任務偶爾暫停I / O或用戶交互,那么正確的數字可能會稍大一些。 但總的來說,如果在任何一個時刻有更多的線程想要進行計算而不是可用的核心,那么你的程序就會浪費時間在上下文切換上 - 也就是說,調度會讓你付出代價。

為了最快地完成大部分工作:我最好在我需要做更多工作時啟動更多線程,讓Java線程調度程序處理分配工作,或者更聰明地管理工作負載以保持CPU低於100%讓我更快?

隨着您添加越來越多的線程,上下文切換,內存緩存刷新,內存緩存溢出以及內核和JVM線程管理所產生的開銷也會增加。 當您的線程占用CPU時,它們的內核優先級降至最低,並且它們將達到時間片最小值。 隨着越來越多的線程占用內存,它們會溢出各種內部CPU內存緩存。 CPU需要從較慢的內存中交換作業的可能性更高。 JVM內部存在更多互斥鎖本地爭用,可能還有一些(可能很小的)增量每線程和對象帶寬GC開銷。 根據用戶任務的同步程度,更多線程會導致內存刷新和鎖爭用增加。

對於任何程序和任何體系結構,線程都可以最佳地利用可用的處理器和IO資源,同時限制內核和JVM開銷。 反復尋找最佳位置將需要多次迭代和一些猜測。

我建議使用Executors.newFixedThreadPool(SOME_NUMBER); 並向你提交工作。 然后,您可以執行多次運行,根據工作和框架的結構,上下調整線程數,直到找到同時運行的最佳池數。

但是要理解,最佳線程數將根據處理器的數量和可能非常重要的其他因素而變化。 如果它們阻塞磁盤或網絡IO資源,則可能需要更多線程。 如果他們正在做的工作主要是基於CPU的,那么線程就會減少。

你的CPU以100%運行的事實並沒有說明他們在做有用工作的忙碌程度。 在您的情況下,您使用的線程數多於核心數,因此100%包含一些上下文切換並且不必要地使用內存(對100個線程影響很小),這是次優的。

對於CPU密集型任務,我通常使用這個成語:

private final int NUM_THREADS = Runtime.getRuntime().availableProcessors() + 1;
private final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

正如其他人所指出的,使用更多線程只會引入不必要的上下文切換。

顯然,如果任務執行某些I / O和其他阻塞操作,則這不適用,並且更大的池將有意義。

編輯

要回復@MartinJames評論,我已經運行了一個(簡單化)基准測試 - 結果顯示,從池大小=處理器數量+ 1到100只會略微降低性能(讓我們稱之為5%) - 更高的數字( 1000和10000)確實顯着地達到了性能。

結果是10次運行的平均值:
泳池大小:9:238毫秒。 //(NUM_CORES + 1)
泳池大小:100:245毫秒。
泳池大小:1000:319毫秒。
池大小:10000:2482毫秒。

碼:

public class Test {

    private final static int NUM_CORES = Runtime.getRuntime().availableProcessors();
    private static long count;
    private static Runnable r = new Runnable() {

        @Override
        public void run() {
            int count = 0;
            for (int i = 0; i < 100_000; i++) {
                count += i;
            }
            Test.count += count;
        }
    };

    public static void main(String[] args) throws Exception {
        //warmup
        runWith(10);

        //test
        runWith(NUM_CORES + 1);
        runWith(100);
        runWith(1000);
        runWith(10000);
    }

    private static void runWith(int poolSize) throws InterruptedException {
        long average = 0;
        for (int run = 0; run < 10; run++) { //run 10 times and take the average
            Test.count = 0;
            ExecutorService executor = Executors.newFixedThreadPool(poolSize);
            long start = System.nanoTime();
            for (int i = 0; i < 50000; i++) {
                executor.submit(r);
            }
            executor.shutdown();
            executor.awaitTermination(10, TimeUnit.SECONDS);
            long end = System.nanoTime();
            average += ((end - start) / 1000000);
            System.gc();
        }
        System.out.println("Pool size: " + poolSize + ": " + average / 10 + " ms.  ");
    }
}

“變得更聰明並且管理工作量以保持CPU低於100%會讓我更快嗎?”

可能不是。

正如其他人發布的那樣,如果大多數任務都是CPU密集型的話,那么線程池中有100個線程太多了。 它對典型系統的性能沒有太大影響 - 如果過多則會導致4個線程壞,400個壞。

你是如何決定100個線程的? 為什么不16,說?

'線程數量不大,比如高達100' - 它有變化嗎? 只需在啟動時創建16並停止管理它們 - 只需將隊列傳遞給它們並忘記它們。

可怕的想法 - 你沒有為每項任務創建一個新線程,是嗎?

您應該保持100%的使用率,但線程數最少。 100個線程看起來太多了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM