private void AnalyzeBase(RawData rawData, IProgress <ProgressInfo> progress, CancellationToken cancellationToken) { var score = CombineLocationData(rawData.BlockOffsetMultiple, rawData.Data).ToArray(); if (!score.Any()) { return; } cancellationToken.ThrowIfCancellationRequested(); // ------------------------ // Process scores of steps. // ------------------------ Dictionary <double, double> diskScoresStepAveraged; try { if (rawData.BlockOffsetMultiple == 0) // If initial step of run { // Store initial score. _diskScoresStep = score; _diskScoresStepCount = 1; diskScoresStepAveraged = _diskScoresStep.ToDictionary(pair => pair.Key, pair => pair.Value); } else { // Combine scores into one sequential score. _diskScoresStep = _diskScoresStep .Concat(score) .OrderBy(x => x.Key) // Order by locations. .ToArray(); _diskScoresStepCount++; // Prepare score of averaged transfer rates (values) of locations (keys) within block size length. diskScoresStepAveraged = new Dictionary <double, double>(); var blockSizeMibiBytes = (double)Settings.Current.BlockSize / 1024D; // Convert KiB to MiB. foreach (var data in _diskScoresStep.Select((body, index) => new { body, index })) { var dataCopy = data; var valueList = new List <double>(); var index = dataCopy.index; var keyBottom = dataCopy.body.Key - blockSizeMibiBytes; do { valueList.Add(_diskScoresStep[index].Value); index--; } while ((0 <= index) && (keyBottom < _diskScoresStep[index].Key)); if (Settings.Current.RemovesOutlier && (3 <= valueList.Count)) // Removing outliers from data less than 3 makes no sense. { // Remove outliers using average and standard deviation. valueList = RemoveOutlier(valueList, 2).ToList(); // This multiple (2) is to be considered. } diskScoresStepAveraged.Add(dataCopy.body.Key, valueList.Average()); } } } catch (Exception ex) { Debug.WriteLine("Failed to process scores of steps. {0}", ex); throw; } cancellationToken.ThrowIfCancellationRequested(); // ----------------------- // Process scores of runs. // ----------------------- try { if (!_diskScoresRun.Any()) // If initial step of initial run. { // Store initial score. _diskScoresRun.Add(diskScoresStepAveraged); progress.Report(new ProgressInfo(_diskScoresRun[0])); } else { if (rawData.BlockOffsetMultiple != 0) // If not initial step of run. { // Remove existing score of that run. _diskScoresRun.RemoveAt(_diskScoresRun.Count - 1); } _diskScoresRun.Add(diskScoresStepAveraged); // Prepare score of list of transfer rates (values) of the same locations (keys) over all runs. var diskScoresRunAll = _diskScoresRun .AsParallel() .SelectMany(dict => dict) .ToLookup(pair => pair.Key, pair => pair.Value); // Prepare score of averaged transfer rates (values) over all runs. var diskScoresRunAllAveraged = new Dictionary <double, double>(); foreach (var data in diskScoresRunAll) { var dataArray = data.ToArray(); if (Settings.Current.RemovesOutlier && (3 <= dataArray.Length)) // Removing outliers from data less than 3 makes no sense. { // Remove outliers using average and standard deviation. dataArray = RemoveOutlier(dataArray, 2).ToArray(); // This multiple (2) is to be considered. } diskScoresRunAllAveraged.Add(data.Key, dataArray.Average()); } progress.Report(new ProgressInfo(diskScoresRunAllAveraged)); } } catch (Exception ex) { Debug.WriteLine("Failed to process scores of runs. {0}", ex); throw; } }
/// <summary> /// Read disk by P/Invoke (synchronously and with cancellation). /// </summary> /// <param name="rawData">Raw data</param> /// <param name="cancellationToken">Cancellation token</param> /// <returns>Result data</returns> internal static RawData ReadDiskPInvoke(RawData rawData, CancellationToken cancellationToken) { var blockOffsetMultiple = rawData.BlockOffsetMultiple; SafeFileHandle hFile = null; try { // ---------- // Read disk. // ---------- // This section is based on sequential read test of CrystalDiskMark (3.0.2) // created by hiyohiyo (http://crystalmark.info/). // Get handle to disk. hFile = NativeMethod.CreateFile( String.Format(@"\\.\PhysicalDrive{0}", Settings.Current.PhysicalDrive), NativeMethod.GENERIC_READ, // Administrative privilege is required. 0, IntPtr.Zero, NativeMethod.OPEN_EXISTING, NativeMethod.FILE_ATTRIBUTE_NORMAL | NativeMethod.FILE_FLAG_NO_BUFFERING | NativeMethod.FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero); if (hFile == null || hFile.IsInvalid) { // This is normal when this application is not run by administrator. throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get handle to disk."); } // Prepare parameters. var areaSizeActual = Settings.Current.AreaSize; // Area size for actual reading (MiB) if (0 < Settings.Current.BlockOffset) { areaSizeActual -= 1; // 1 is for the last MiB of area. If offset, it may exceed disk size. } int readNum = (areaSizeActual * 1024) / Settings.Current.BlockSize; // The number of reads int loopOuter = 1; // The number of outer loops int loopInner = readNum; // The number of inner loops if (Settings.Current.AreaRatioInner < Settings.Current.AreaRatioOuter) { loopOuter = (areaSizeActual * 1024) / (Settings.Current.BlockSize * Settings.Current.AreaRatioOuter); loopInner = Settings.Current.AreaRatioInner; readNum = loopInner * loopOuter; } var areaLocationBytes = (long)Settings.Current.AreaLocation * 1024L * 1024L; // Bytes var blockOffsetBytes = (long)Settings.Current.BlockOffset * (long)blockOffsetMultiple * 1024L; // Bytes var jumpBytes = (long)Settings.Current.BlockSize * (long)Settings.Current.AreaRatioOuter * 1024L; // Bytes areaLocationBytes += blockOffsetBytes; var buffSize = (uint)Settings.Current.BlockSize * 1024U; // Buffer size (Bytes) var buff = new byte[buffSize]; // Buffer uint readSize = 0U; var sw = new Stopwatch(); var lapTime = new TimeSpan[readNum + 1]; // 1 is for leading zero time. lapTime[0] = TimeSpan.Zero; // Leading zero time for (int i = 0; i < loopOuter; i++) { if (0 < i) { areaLocationBytes += jumpBytes; } // Move pointer. var result1 = NativeMethod.SetFilePointerEx( hFile, areaLocationBytes, IntPtr.Zero, NativeMethod.FILE_BEGIN); if (result1 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to move pointer."); } // Measure disk transfer rate (sequential read). for (int j = 1; j <= loopInner; j++) { cancellationToken.ThrowIfCancellationRequested(); sw.Start(); var result2 = NativeMethod.ReadFile( hFile, buff, buffSize, ref readSize, IntPtr.Zero); if (result2 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to measure disk transfer rate."); } sw.Stop(); lapTime[i * loopInner + j] = sw.Elapsed; } } cancellationToken.ThrowIfCancellationRequested(); // ---------------- // Process results. // ---------------- // Calculate each transfer rate. var data = new double[readNum]; for (int i = 1; i <= readNum; i++) { var timeEach = (lapTime[i] - lapTime[i - 1]).TotalSeconds; // Second var scoreEach = Math.Floor(buffSize / timeEach) / 1000000D; // MB/s data[i - 1] = scoreEach; } // Calculate total transfer rate (just for reference). var totalTime = lapTime[readNum].TotalSeconds; // Second var totalRead = (double)Settings.Current.BlockSize * (double)readNum * 1024D; // Bytes var totalScore = Math.Floor(totalRead / totalTime) / 1000000D; // MB/s // Compose outcome. var outcome = "[Start data]" + Environment.NewLine; int k = 0; for (int i = 0; i < readNum; i++) { outcome += String.Format("{0:f6} ", data[i]); // Data have 6 decimal places. k++; if ((k == 6) | (i == readNum - 1)) { k = 0; outcome += Environment.NewLine; } } outcome += "[End data]" + Environment.NewLine; outcome += String.Format("Total {0:f6} MB/s", totalScore); rawData.Result = ReadResult.Success; rawData.Outcome = outcome; rawData.Data = data; } catch (Win32Exception ex) { rawData.Result = ReadResult.Failure; rawData.Message = String.Format("{0} (Code: {1}).", ex.Message.Substring(0, ex.Message.Length - 1), ex.ErrorCode); } catch (Exception ex) // Including OperationCanceledException { rawData.Result = ReadResult.Failure; rawData.Message = ex.Message; } finally { if (hFile != null) { // CloseHandle is inappropriate to close SafeFileHandle. // Dispose method is not necessary because Close method will call it internally. hFile.Close(); } } return(rawData); }
/// <summary> /// Read disk by native (synchronously). /// </summary> /// <param name="rawData">Raw data</param> /// <returns>Result data</returns> internal static RawData ReadDiskNative(RawData rawData) { if (!NativeExeExists) { rawData.Result = ReadResult.Failure; rawData.Message = String.Format("Cannot find {0}.", _nativeExeFile); return(rawData); } var blockOffsetMultiple = rawData.BlockOffsetMultiple; try { var arguments = String.Format("{0} {1} {2} {3} {4}", Settings.Current.PhysicalDrive, Settings.Current.BlockSize, Settings.Current.BlockOffset * blockOffsetMultiple, Settings.Current.AreaSize, Settings.Current.AreaLocation); if (Settings.Current.AreaRatioInner < Settings.Current.AreaRatioOuter) { arguments += String.Format(" {0} {1}", Settings.Current.AreaRatioInner, Settings.Current.AreaRatioOuter); } using (_readProcess = new Process { StartInfo = { FileName = _nativeExePath, Verb = "RunAs", // Run as administrator. Arguments = arguments, UseShellExecute = false, CreateNoWindow = true, //WindowStyle = ProcessWindowStyle.Hidden, RedirectStandardOutput = true, } }) { _readProcess.Start(); var outcome = _readProcess.StandardOutput.ReadToEnd(); _readProcess.WaitForExit(); rawData.Result = (_readProcess.HasExited & (_readProcess.ExitCode == 0)) ? ReadResult.Success : ReadResult.Failure; rawData.Outcome = outcome; switch (rawData.Result) { case ReadResult.Success: rawData.Data = FindData(outcome); break; case ReadResult.Failure: rawData.Message = FindMessage(outcome); break; } } } catch (Exception ex) { rawData.Result = ReadResult.Failure; rawData.Message = String.Format("Failed to execute {0}. {1}", _nativeExeFile, ex.Message); } return(rawData); }
/// <summary> /// Read disk by P/Invoke (asynchronously and with cancellation). /// </summary> /// <param name="rawData">Raw data</param> /// <param name="cancellationToken">Cancellation token</param> /// <returns>Result data</returns> internal static async Task <RawData> ReadDiskPInvokeAsync(RawData rawData, CancellationToken cancellationToken) { return(await Task.Run(() => ReadDiskPInvoke(rawData, cancellationToken), cancellationToken)); }