我有一個用 .NET 6 撰寫的 C# 控制臺程式。我首先撰寫了代碼,以便它在一個執行緒中按順序運行。有用。如果我查看任務管理器,我的 16 個邏輯處理器中的一個非常繁忙,其余的基本上什么都不做。這正是我所期望的。執行實際作業的子程式(好吧,它實際上是一個靜態 void 方法)運行了數千次。我更改了代碼,以便在回圈中將任務排隊并將其添加到串列中:
Task t = Task.Run(() => SeekAnswer(param));
tasks.Add(t);
排隊回圈完成后,我們等待所有并行執行緒完成:
Task.WaitAll(tasks.ToArray());
該代碼有效,并給出與順序代碼相同的答案。耶。但它只比順序代碼快 30%。我期待像 5 甚至 10 倍的加速。如果我查看任務管理器,我會發現每個邏輯處理器都在 20% 左右閑逛,并且不再有一個邏輯處理器是 100% 忙碌的。因為分析任務SeekAnswer僅受 CPU 限制,我預計每個邏輯處理器都將處于 100% 的狀態,并且機器總體上會開始感覺遲緩。 SeekAnswer沒有 I/O,沒有磁盤活動,沒有網路,沒有 yield 或 await 或類似的東西。 SeekAnswer運行大約需要 500 毫秒,完成后,它將結果添加到串列中。
一種猜測是,因為它是一個控制臺應用程式,控制臺部分正在竊取等待查看是否有滑鼠移動或鍵盤輸入或其他東西的周期。如果我進入任務管理器并將其優先級設定為實時,則每個處理器可能會從 20% 變為 23%。另一個猜測是 C# 任務管理器/CLR 無法完全加載英特爾超執行緒邏輯處理器。
我的問題是是什么阻止我更充分地利用處理器?
uj5u.com熱心網友回復:
您當前的解決方案將作業分成 N 個任務。通常,您應該更喜歡更高級別的抽象。Parallel或者 PLINQ 可能比您當前的“每個任務呼叫一次”解決方案更有效地將您的作業劃分為任務。
當 SeekAnswer 有答案時,它會添加到一個公共串列中。
洗掉所有協調。如果您正在收集結果,那么只需回傳結果并使用 PLINQ 之類的東西來收集它們。
一種猜測是,因為它是一個控制臺應用程式,控制臺部分正在竊取等待查看是否有滑鼠移動或鍵盤輸入或其他東西的周期。
不,它不會那樣做。它會監聽信號,但不會進行輪詢。即使是輪詢,也會顯示為 CPU 使用率。
如果我進入任務管理器并將其優先級設定為實時,則每個處理器可能會從 20% 變為 23%。
CPU-bound 任務——相當違反直覺——應該以較低的優先級運行。作為所有現代 Windows 作業系統基礎的 NT 調度系統將自動為您調整此優先級。
另一個猜測是 C# 執行緒管理器/CLR 無法完全加載英特爾超執行緒邏輯處理器。
不是 CLR 的限制。
了解超執行緒的實際作業原理很重要:只有一個內核可以執行代碼指令。超執行緒處理器通過復制指令流水線來作業,僅此而已。每個內核一次仍然只運行一條指令。當指令必須執行諸如記憶體讀取之類的操作并且通常會使處理器停止時,超執行緒的好處就來了。在這種情況下,超執行緒可以切換到不同的邏輯處理器并繼續其指令流水線。
TL;DR:如果您的作業永遠不會停滯(即,它適合 CPU 快取),那么超執行緒無法提供任何加速。無論語言/運行時/作業系統如何,都是如此。
您可能正在等待一些執行緒注入。對于控制臺應用程式,應將最小執行緒 數設定為處理器數量,但這些執行緒用于一些內務處理任務。您可以嘗試將最小(作業)執行緒數設定得更高。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/513755.html
下一篇:使用多執行緒/多核行程防止抖動
