我正在尝试在RAID-5中使用8个SSD的数据流应用程序中获得最高I / O性能(每个SSD通告并提供500 MB /秒读取)。
我使用64KB缓冲区创建FileStream,并以阻塞方式读取多个块(不打算使用)。这就是我现在拥有的80K 20K文件,没有片段: 传统阻塞读取的单线程为1270 MB /秒,6线程为1556 MB /秒。
我发现单线程的注意事项是内核花费了单个核心的CPU时间(在12个核心的Process Explorer中为8.3%红色)。使用6个线程,在内核中花费大约5倍的CPU时间(在Process Explorer中有12个内核,占用41%的红色)。
我真的希望避免I / O绑定场景中多线程应用程序的复杂性。
是否可以在单线程应用程序中实现这些传输速率?那么,什么是减少内核模式中的时间量的好方法?
如果有的话,C#中的新异步功能会如何帮助?
为了进行比较,ATTO disk benchmark在这些硬件上以这些块大小显示2500 MB /秒且CPU利用率低。但是,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)
这里有很多问题。
首先,我发现您并未尝试使用非缓存I / O.这意味着系统将尝试将数据缓存在RAM中,并将服务读取出来。所以你可以从中获得额外的数据传输。做非缓存的I / O.
接下来,您似乎正在创建/销毁循环内的线程。这是低效的。
最后,您需要调查数据的对齐方式。跨越读取块边界可能会增加成本。
我主张使用非缓存的异步I / O.我不确定如何在C#中实现这一点(但它应该很容易)。
编辑:另外,你为什么使用RAID 5?除非数据是一次写入,否则这可能会在SSD上产生可怕的性能。值得注意的是,擦除块大小通常为512K,这意味着当您编写较小的内容时,SSD将需要读取其固件中的512K,更改数据,然后将其写入其他位置。您可能希望使条带大小=擦除块的大小。此外,您应该检查写入的对齐方式。