From 4fb32d6abc6e80652354a6c638968cb832c7572f Mon Sep 17 00:00:00 2001 From: ArtiomPatov Date: Wed, 9 Nov 2022 13:01:03 +0300 Subject: [PATCH 1/2] Initial commit --- test1/test1.sln | 16 +++ test1/test1/CheckSumCalculator.cs | 158 ++++++++++++++++++++++++++++++ test1/test1/Program.cs | 9 ++ test1/test1/test1.csproj | 10 ++ 4 files changed, 193 insertions(+) create mode 100644 test1/test1.sln create mode 100644 test1/test1/CheckSumCalculator.cs create mode 100644 test1/test1/Program.cs create mode 100644 test1/test1/test1.csproj diff --git a/test1/test1.sln b/test1/test1.sln new file mode 100644 index 0000000..c58cf79 --- /dev/null +++ b/test1/test1.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test1", "test1\test1.csproj", "{47A8A911-5B5E-448F-933A-0FAF7D3211B3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {47A8A911-5B5E-448F-933A-0FAF7D3211B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47A8A911-5B5E-448F-933A-0FAF7D3211B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47A8A911-5B5E-448F-933A-0FAF7D3211B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47A8A911-5B5E-448F-933A-0FAF7D3211B3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/test1/test1/CheckSumCalculator.cs b/test1/test1/CheckSumCalculator.cs new file mode 100644 index 0000000..e27f703 --- /dev/null +++ b/test1/test1/CheckSumCalculator.cs @@ -0,0 +1,158 @@ +namespace test1; + +using System.Security.Cryptography; + +/// +/// Calculates check sum of specific directory or file. +/// +public static class CheckSumCalculator +{ + /// + /// Calculates check sum in one thead mode. + /// + /// Path of file or directory. + /// Check sum. + /// Throws if path does not point to file or directory. + public static byte[] CalculateCheckSumSerially(string path) + { + if (File.Exists(path)) + { + return CalculateFileCheckSum(path); + } + if (Directory.Exists(path)) + { + return CalculateDirectoryHashSerially(path); + } + + throw new FileNotFoundException("Invalid path."); + } + + /// + /// Calculates check sum using multiple threads. + /// + /// Path of file or directory. + /// Check sum. + /// Throws if path does not point to file or directory. + public static byte[] CalculateCheckSumConcurrently(string path) + { + if (File.Exists(path)) + { + return CalculateFileCheckSum(path); + } + if (Directory.Exists(path)) + { + return CalculateDirectoryHashConcurrently(path); + } + + throw new FileNotFoundException("Invalid path."); + } + + /// + /// Calculates check sum of specific file. + /// + /// Path of the file. + /// Check sum of the file. + private static byte[] CalculateFileCheckSum(string path) + { + using var file = File.OpenRead(path); + using var md5 = MD5.Create(); + + return md5.ComputeHash(file); + } + + private static string[] GetSortedItems(string path) + { + var files = Directory.GetFiles(path); + var directories = Directory.GetDirectories(path); + var items = files.Concat(directories).ToArray(); + Array.Sort(items); + + return items; + } + + private static byte[] CalculateDirectoryHashSerially(string path) + { + var items = GetSortedItems(path); + var hashes = new List(); + + foreach (var item in items) + { + if (File.Exists(item)) + { + CalculateFileCheckSum(item).ToList().ForEach(x => hashes.Add(x)); + } + else + { + CalculateDirectoryHashSerially(item).ToList().ForEach(x => hashes.Add(x)); + } + } + + using var md5 = MD5.Create(); + + return md5.ComputeHash(hashes.ToArray()); + } + + private static byte[] CalculateDirectoryHashConcurrently(string path) + { + var items = GetSortedItems(path); + var hashes = new SortedDictionary(); + var numberOfThreads = Environment.ProcessorCount / 2 < 2 ? 2 : Environment.ProcessorCount / 2; + var counter = 0; + var locker = new object(); + + for (var i = 0; i < numberOfThreads; i++) + { + var startingIndex = i; + Task.Run(() => + { + var localItems = Array.Empty(); + + for (var j = startingIndex; + j < (startingIndex == numberOfThreads - 1 + ? items.Length + : (startingIndex + 1) * items.Length / numberOfThreads); + j++) + { + localItems[j - startingIndex] = items[j]; + } + + foreach (var item in localItems) + { + if (File.Exists(item)) + { + var hash = CalculateFileCheckSum(item); + lock (hashes) + { + hashes.Add(item, hash); + } + } + + else + { + var hash = CalculateDirectoryHashSerially(item); + lock (hashes) + { + hashes.Add(item, hash); + } + } + } + + Interlocked.Increment(ref counter); + if (counter == numberOfThreads) + { + Monitor.Pulse(locker); + } + }); + } + + while (counter != numberOfThreads) + { + Monitor.Wait(locker); + } + + var hashesList = hashes.Values.SelectMany(value => value).ToList(); + using var md5 = MD5.Create(); + + return md5.ComputeHash(hashesList.ToArray()); + } +} \ No newline at end of file diff --git a/test1/test1/Program.cs b/test1/test1/Program.cs new file mode 100644 index 0000000..a57f276 --- /dev/null +++ b/test1/test1/Program.cs @@ -0,0 +1,9 @@ +using test1; + +if (args.Length != 1) +{ + return; +} + +var hash = CheckSumCalculator.CalculateCheckSumConcurrently(args[0]); +Console.WriteLine(BitConverter.ToString(hash)); \ No newline at end of file diff --git a/test1/test1/test1.csproj b/test1/test1/test1.csproj new file mode 100644 index 0000000..b9de063 --- /dev/null +++ b/test1/test1/test1.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + From 798017cd8f11638e04916617179548eea680a089 Mon Sep 17 00:00:00 2001 From: ArtiomPatov Date: Wed, 9 Nov 2022 13:09:58 +0300 Subject: [PATCH 2/2] Add comparison --- test1/test1/Comparison.cs | 25 +++++++++++++++++++++++++ test1/test1/Program.cs | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 test1/test1/Comparison.cs diff --git a/test1/test1/Comparison.cs b/test1/test1/Comparison.cs new file mode 100644 index 0000000..084d03f --- /dev/null +++ b/test1/test1/Comparison.cs @@ -0,0 +1,25 @@ +namespace test1; + +/// +/// Tools for comparison. +/// +public static class Comparison +{ + /// + /// Calculates standard deviation using time of calculations. + /// + /// Array, which elements are time of calculations. + /// Returns double value -- rounded to one decimal place standard deviation of the given data. + public static double CalculateDeviation(long[] calculationTime) + { + var expectedValue = calculationTime.Average(); + double variance = 0; + + for (var i = 0; i < calculationTime.Length; i++) + { + variance += Math.Pow(calculationTime[i] - expectedValue, 2) / calculationTime.Length; + } + + return Math.Round(Math.Sqrt(variance), 1); + } +} diff --git a/test1/test1/Program.cs b/test1/test1/Program.cs index a57f276..ed74262 100644 --- a/test1/test1/Program.cs +++ b/test1/test1/Program.cs @@ -1,9 +1,38 @@ -using test1; +using System.Diagnostics; +using test1; if (args.Length != 1) { return; } -var hash = CheckSumCalculator.CalculateCheckSumConcurrently(args[0]); -Console.WriteLine(BitConverter.ToString(hash)); \ No newline at end of file +const int n = 10; + +var calculationTime = new long[n]; +for (var i = 0; i < n; i++) +{ + var stopwatch = new Stopwatch(); + stopwatch.Start(); + CheckSumCalculator.CalculateCheckSumSerially(args[0]); + stopwatch.Stop(); + calculationTime[i] = stopwatch.ElapsedMilliseconds; +} + +var expectedValue1 = calculationTime.Average(); +var deviation1 = Comparison.CalculateDeviation(calculationTime); +Console.WriteLine(expectedValue1); +Console.WriteLine(deviation1); + +for (var i = 0; i < n; i++) +{ + var stopwatch = new Stopwatch(); + stopwatch.Start(); + CheckSumCalculator.CalculateCheckSumConcurrently(args[0]); + stopwatch.Stop(); + calculationTime[i] = stopwatch.ElapsedMilliseconds; +} + +var expectedValue2 = calculationTime.Average(); +var deviation2 = Comparison.CalculateDeviation(calculationTime); +Console.WriteLine(expectedValue2); +Console.WriteLine(deviation2);