我有一個包含 125 行的檔案:
blue
black
yellow
...
purple
我想創建 5 個執行緒,這 5 個執行緒將占用檔案中的 25 行不同的行并將它們列印到控制臺視窗,只要它們列印每一行,它們是否不按升序列印并不重要。
我試過的代碼如下所示:
string[] colors = File.ReadAllLines("colors.txt");
Thread[] threads = new Thread[5];
Console.WriteLine(threads.Length); // 5
for (int i = 0; i < threads.Length; i )
{
int indexStart = (colors.Length) * i / threads.Length;
int indexStop = (colors.Length) * (i 1) / threads.Length;
new Thread(() =>
{
for (int j = indexStart; j < indexStop; j )
{
Console.WriteLine(colors[j]);
}
}).Start();
}
Console.ReadLine();
看起來運行程式時它和單執行緒程式一樣快,我做錯了什么?
uj5u.com熱心網友回復:
我想這是因為只有一個執行緒可以訪問控制臺以在給定時刻寫出結果。當然,控制臺是執行緒安全的,它們不會相互干擾,但您不可能同時從 2 個或更多執行緒寫入。
uj5u.com熱心網友回復:
一般來說,在任何語言中處理輸入/輸出時,多執行緒不會帶來性能提升。所有型別的 I/O 往往是串行的——SSD 上只有一個 SATA 通道,NIC 只連接了一根以太網電纜,只有一個 GPU 渲染控制臺視窗,等等。每條資料輸入/輸出都必須去通過那個單一的界面。
幾乎根據定義,與 CPU 及其記憶體相比,I/O 是慢的,因此讓多個執行緒嘗試訪問 I/O 資源不會導致該資源作業得更快。例如,SSD 的速度可能看起來很快,但與 CPU 及其記憶體相比,它的速度非常慢。
通常,在這種情況下,讓多個執行緒爭用有限的 I/O 資源實際上會稍微減慢速度;在 I/O 設備服務的執行緒之間切換時會丟失時間。
何時穿線,或不穿線?
多執行緒編程的藝術是知道應用程式何時會受到 I/O 限制或計算限制。如果受到 I/O 性能的限制,添加執行緒是沒有意義的。如果受限于單個 CPU 內核的速度,添加執行緒是有價值的。
然而,它并沒有就此結束,因為 CPU 的主記憶體本身就是一個 I/O 設備(記憶體總線末端的 RAM 芯片),CPU 的快取也是如此。如果您的“作業”對于每個資料項來說都是微不足道的,例如簡單地將一個大陣列中的數字相加,那么您很可能最終將 I/O 限制在主記憶體上;資料無法足夠快地從記憶體中轉移到 CPU 中,CPU 最終只能等待資料到達。添加更多執行緒沒有意義,因為它們不會導致主記憶體運行得更快。
CPU、高速快取和記憶體之間的互動會變得非常復雜;例如,在 PowerPC 上,您可以(在適當的情況下)通過將資料乘以 1 來獲得更多的性能。您可能認為是無意義的作業,但額外的操作可能會導致快取和記憶體更好地互動,從而導致更快的結果.
因此,知道何時進行執行緒化以及何時不進行執行緒化(為了提高演算法吞吐量)意味著在必須將結果寫回到主記憶體。
你的目標是擁有一定數量的資料和代碼,它們都適合 L1 快取,在結果必須寫回到記憶體中的其他地方或新資料之前,對這些資料執行盡可能多的有用操作需要從記憶體的其他部分。代碼對 L1 快取中的資料所做的有用作業越多,L1 快取未命中(和相關延遲)就越少。那樣的話,額外的執行緒會增加性能,因為在多核 CPU 上,所有內核都可以很好地運轉,不會經常爭用對 L3 快取或主記憶體的訪問。
C# 使這更難的地方在于,作為一種垃圾收集語言,作為行程的一部分運行的代碼比您撰寫的源代碼(如 GC)要多。對于任何給定的代碼行,也很難確切地知道會發生什么(就記憶體操作而言)。所有這些都會打亂蘋果車。
在 C 中執行此操作的一個好處是代碼在代碼中非常清楚代碼對記憶體的作用。很少有東西被語言的語法所隱藏。
細胞處理器
好的,所以如果您正在開發這樣一段代碼,并且如果您必須估計完成的作業等等以將演算法分解為執行緒,那么什么有幫助,什么會阻礙?好吧,實際上,當今幾乎所有 CPU(ARM、Intel、AMD 等)中的 SMP 實作都是一個障礙,因為很難完全理解內核、快取和記憶體子系統將執行的所有操作。英特爾提供了一些軟體工具來幫助處理此類事情——VTune Profiler——幫助開發人員了解一段代碼如何與 CPU、快取等互動。
這就是 Cell 處理器(如 Sony Play Station 3 中的處理器)的優勢所在。它有多個數學核心,但沒有快取,也沒有 SMP。否則可能被快取的硅片是用于 8 個獨立 SPE 內核(數學內核)的片上 RAM。開發人員必須明確撰寫代碼以將資料和代碼從記憶體加載到內核中,它必須適合并運行在這個有限數量的片上記憶體中,并將結果寫回到主記憶體(或另一個內核的記憶體中) . 或者結果可以保持不變,新代碼加載到核心中。
這聽起來很復雜,但它很好,因為你不必了解芯片將以高速快取、記憶體的方式做什么;它什么也沒做。因此,實際上,這簡化了將演算法分解為核心大小塊的任務,因為您的分解要么適合固定數量的每核心記憶體,要么不適合。如果它確實適合,那將是閃電般的快速和可擴展的。如果沒有,它根本不會運行。好的,所以很難做到這一點,但是當您做到時,結果非常好。
為了獲得絕對的性能,英特爾花了很長時間才生產出可以與 Cell 處理器匹敵的 x64 CPU。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/537621.html
標籤:C#多线程表现
上一篇:根據@input設定組件提供者
