我試圖在具有RAID-5中8個SSD(每個SSD通告並提供500 MB /秒讀取次數)的數據流應用程序中獲得最佳I/O性能。如何通過單線程應用程序中的FileStream提高吞吐量
我創建了帶有64KB緩衝區的FileStream,並以阻塞的方式讀取了許多塊(雙關語並非意圖)。下面是我現在在20K文件中使用80GB的情況,沒有碎片: 傳統的阻塞讀取在單線程時爲1270 MB /秒,在6線程時爲1556 MB /秒。
我在單線程中注意到的是單個內核的CPU時間花費在內核上(在12個內核的Process Explorer中爲8.3%紅色)。使用6個線程,內核消耗大約5倍的CPU時間(在12核心的Process Explorer中,紅色程度爲41%)。
我真的想避免I/O綁定場景中多線程應用程序的複雜性。
是否有可能在單線程應用程序中實現這些傳輸速率?也就是說,在內核模式下減少時間的好方法是什麼?
如果有的話,C#中的新Async特性如何?
作爲比較,ATTO disk benchmark在此硬件上的這些塊大小和CPU利用率較低時顯示爲2500 MB /秒。但是,ATTO數據集大小僅爲2GB。
使用LSI 9265-8i RAID控制器,64k條帶大小,64k羣集大小。
下面是使用代碼的草圖。我不用這種方式編寫生產代碼,它只是一個概念證明。
volatile bool _somethingLeftToRead = false;
long _totalReadInSize = 0;
void ProcessReadThread(object obj)
{
TestThreadJob job = obj as TestThreadJob;
var dirInfo = new DirectoryInfo(job.InFilePath);
int chunk = job.DataBatchSize * 1024;
//var tile = new List<byte[]>();
var sw = new Stopwatch();
var allFiles = dirInfo.GetFiles();
var fileStreams = new List<FileStream>();
long totalSize = 0;
_totalReadInSize = 0;
foreach (var fileInfo in allFiles)
{
totalSize += fileInfo.Length;
var fileStream = new FileStream(fileInfo.FullName,
FileMode.Open, FileAccess.Read, FileShare.None, job.FileBufferSize * 1024);
fileStreams.Add(fileStream);
}
var partial = new byte[chunk];
var taskParam = new TaskParam(null, partial);
var tasks = new List<Task>();
int numTasks = (int)Math.Ceiling(fileStreams.Count * 1.0/job.NumThreads);
sw.Start();
do
{
_somethingLeftToRead = false;
for (int taskIndex = 0; taskIndex < numTasks; taskIndex++)
{
if (_threadCanceled)
break;
tasks.Clear();
for (int thread = 0; thread < job.NumThreads; thread++)
{
if (_threadCanceled)
break;
int fileIndex = taskIndex * job.NumThreads + thread;
if (fileIndex >= fileStreams.Count)
break;
var fileStream = fileStreams[fileIndex];
taskParam.File = fileStream;
if (job.NumThreads == 1)
ProcessFileRead(taskParam);
else
tasks.Add(Task.Factory.StartNew(ProcessFileRead, taskParam));
//tile.Add(partial);
}
if (_threadCanceled)
break;
if (job.NumThreads > 1)
Task.WaitAll(tasks.ToArray());
}
//tile = new List<byte[]>();
}
while (_somethingLeftToRead);
sw.Stop();
foreach (var fileStream in fileStreams)
fileStream.Close();
totalSize = (long)Math.Round(totalSize/1024.0/1024.0);
UpdateUIRead(false, totalSize, sw.Elapsed.TotalSeconds);
}
void ProcessFileRead(object taskParam)
{
TaskParam param = taskParam as TaskParam;
int readInSize;
if ((readInSize = param.File.Read(param.Bytes, 0, param.Bytes.Length)) != 0)
{
_somethingLeftToRead = true;
_totalReadInSize += readInSize;
}
}
固態硬盤尋道時間比硬盤要好得多,但你不能忽視它們。典型值爲0.1到0.3毫秒,在cpu週期中仍然很長。有一堆線程爭奪控制器,等待數據訪問完成並強制它尋找不會得到你ATTO號碼。我懷疑你會用20K的文件,1650聽起來不錯。 –
似乎我在桌子上留下了很多足夠的信息。每個SSD的讀取速度爲500 MB /秒,所以其中7個應該讓我達到3.5 GB /秒,對吧? – GregC
順便說一句,這是超過8通道的SATA 6Gb /秒; RAID卡插入到x8 PCIe 2.0中。 – GregC