2

RAID-5 で 8 つの SSD を使用してデータ ストリーミング アプリケーションで最高の I/O パフォーマンスを得ようとしています (各 SSD はアドバタイズし、500 MB/秒の読み取りを提供します)。

私は 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 ディスク ベンチマークは、このハードウェアと低い CPU 使用率で、これらのブロック サイズで 2500 MB/秒を示しています。ただし、ATTO データセットのサイズはわずか 2GB です。

LSI 9265-8i RAID コントローラを使用し、64k ストライプ サイズ、64k クラスタ サイズ。

画像1

まとめ画像

I/O カウント

QD10

ATTO シングルリクエスト

これは、使用中のコードのスケッチです。私はこの方法で製品コードを書きません。これは単なる概念実証です。

   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;
      }
   }
4

1 に答える 1

1

ここにはいくつかの問題があります。

まず、キャッシュされていない I/O を使用しようとしていないことがわかりました。これは、システムがデータを RAM にキャッシュしようとし、サービスがそれを読み取ろうとすることを意味します。だから、物事から余分なデータ転送が得られます。非キャッシュ I/O を実行します。

次に、ループ内でスレッドを作成/破棄しているようです。これは非効率的です。

最後に、データの配置を調査する必要があります。読み取りブロックの境界を越えると、コストが増加する可能性があります。

非キャッシュの非同期 I/O を使用することをお勧めします。C# でこれを実現する方法がわかりません (ただし、簡単なはずです)。

編集: また、なぜ RAID 5 を使用しているのですか? データが一度だけ書き込み可能でない限り、これは SSD で恐ろしいパフォーマンスを発揮する可能性があります。特に、消去ブロックのサイズは通常 512K です。つまり、これより小さいサイズを書き込む場合、SSD はファームウェアで 512K を読み取り、データを変更してから、別の場所に書き込む必要があります。ストライプ サイズ = 消去ブロックのサイズにしたい場合があります。また、書き込みの配置も確認する必要があります。

于 2012-11-28T17:42:13.797 に答える