我正在尝试在一个数据流应用程序中获得最佳I/O性能,该应用程序具有8个RAID-5固态硬盘(每个固态硬盘通告并提供500 MB/秒的读取)。
我用64KB的缓冲区创建了FileStream,并以阻塞的方式读取了许多块(不是双关语)。这是我现在拥有的20K文件中的80 MB,没有碎片:对于单线程,传统的阻塞读取速度是1270MB/秒,对于6个线程,是1556MB/秒。
我在单线程中注意到的是,单核的CPU时间花费在内核中(在具有12个内核的Process Explorer中,有8.3%是red )。在使用6个线程的情况下,内核占用的CPU时间约为5倍(在具有12个内核的Process Explorer中,CPU占用的时间为41%)。
我真的希望在I/O受限的场景中避免多线程应用程序的复杂性。
是否有可能在单线程应用程序中实现这些传输速率?也就是说,什么是减少内核模式时间的好方法?
如果有的话,C#中的新异步功能会有什么帮助呢?
作为比较,在此硬件上,ATTO disk benchmark在这些块大小下显示为2500 MB/秒,并且CPU利用率较低。然而,ATTO数据集大小仅为2 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;
}
}发布于 2012-11-29 01:42:14
这里有很多问题。
首先,我发现您并没有尝试使用非缓存I/O,这意味着系统将尝试将您的数据缓存到RAM中,并从RAM中读取数据。所以你得到了额外的数据传输。执行非缓存I/O。
接下来,您似乎要在循环中创建/销毁线程。这是低效的。
最后,您需要调查数据的对齐情况。跨越读块边界可能会增加您的成本。
我主张使用非缓存的异步I/O。我不确定如何在C#中实现这一点(但它应该很简单)。
编辑:还有,你为什么要使用RAID 5?除非数据是一次写入,否则这可能会在SSD上产生可怕的性能。值得注意的是,擦除块大小通常为512K,这意味着当您写入较小的数据时,SSD将需要读取其固件中的512K,更改数据,然后将其写入其他位置。您可能希望使条带大小=擦除块的大小。此外,您还应该检查写入的对齐情况。
https://stackoverflow.com/questions/13501664
复制相似问题