diff --git a/Homework1.Tests/Homework1.Tests.csproj b/Homework1.Tests/Homework1.Tests.csproj new file mode 100644 index 0000000..067a0ef --- /dev/null +++ b/Homework1.Tests/Homework1.Tests.csproj @@ -0,0 +1,23 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/Homework1.Tests/MatrixTests.cs b/Homework1.Tests/MatrixTests.cs new file mode 100644 index 0000000..943ecf3 --- /dev/null +++ b/Homework1.Tests/MatrixTests.cs @@ -0,0 +1,68 @@ +namespace Homework1.Tests; + +public class Tests +{ + Matrix matrix1; + Matrix matrix2; + + [SetUp] + public void Setup() + { + matrix1 = new(new int[2, 3] { { 1, 2, 3 }, { 3, 5, 6 } }, 2, 3); + matrix2 = new(new int[3, 2] { { 1, 2 }, { 3, 5 }, { 3, 0 } }, 3, 2); + } + + [Test] + public void ExceptionsTest() + { + Assert.Throws(() => new Matrix(null)); + Assert.Throws(() => Matrix.MultiThreadedMultiplyingMatrixes(null, matrix1)); + Assert.Throws(() => Matrix.MultiThreadedMultiplyingMatrixes(matrix1, null)); + Assert.Throws(() => Matrix.MultiThreadedMultiplyingMatrixes(matrix1, matrix1)); + Assert.Throws(() => Matrix.SequentialMultiplyingMatrixes(null, matrix1)); + Assert.Throws(() => Matrix.SequentialMultiplyingMatrixes(matrix1, null)); + Assert.Throws(() => Matrix.SequentialMultiplyingMatrixes(matrix1, matrix1)); + } + + [Test] + public void EqualCalculationTest() + { + Matrix matrix3 = Matrix.SequentialMultiplyingMatrixes(matrix1, matrix2); + Matrix matrix4 = Matrix.MultiThreadedMultiplyingMatrixes(matrix1, matrix2); + + Assert.Multiple(() => + { + Assert.That(matrix4.Height, Is.EqualTo(matrix3.Height)); + Assert.That(matrix4.Width, Is.EqualTo(matrix3.Width)); + }); + + for (int i = 0; i < matrix4.Height; i++) + { + for (int j = 0; j < matrix3.Width; j++) + { + Assert.That(matrix4.MatrixOfNumbers[i, j], Is.EqualTo(matrix3.MatrixOfNumbers[i, j])); + } + } + } + + [Test] + public void RightCalculationTest() + { + var matrix3 = Matrix.SequentialMultiplyingMatrixes(matrix1, matrix2); + Matrix matrix4 = new(new int[2, 2] { { 16, 12 }, { 36, 31 } }, 2, 2); + + Assert.Multiple(() => + { + Assert.That(matrix4.Height, Is.EqualTo(matrix3.Height)); + Assert.That(matrix4.Width, Is.EqualTo(matrix3.Width)); + }); + + for (int i = 0; i < matrix4.Height; i++) + { + for (int j = 0; j < matrix3.Width; j++) + { + Assert.That(matrix4.MatrixOfNumbers[i, j], Is.EqualTo(matrix3.MatrixOfNumbers[i, j])); + } + } + } +} diff --git a/Homework1.Tests/Usings.cs b/Homework1.Tests/Usings.cs new file mode 100644 index 0000000..9a28bd8 --- /dev/null +++ b/Homework1.Tests/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; diff --git a/Homework1/ComparisonMatrixCountingSpeeds.cs b/Homework1/ComparisonMatrixCountingSpeeds.cs new file mode 100644 index 0000000..e77a0e2 --- /dev/null +++ b/Homework1/ComparisonMatrixCountingSpeeds.cs @@ -0,0 +1,65 @@ +namespace Homework1; + +using System.Diagnostics; + +public static class ComparisonMatrixCountingSpeeds +{ + public static (double, double)[] Experiments(int sizeOfData, int amountOfTestsInSample) //size of data, mathematical expectation, dispersion + { + List timeByThreads = new(); + List timeSequential = new(); + for (int testNumber = 0; testNumber < amountOfTestsInSample; testNumber++) + { + int[,] numbersInMatrix1 = CreateSquareArrayWithRandomValues(sizeOfData); + int[,] numbersInMatrix2 = CreateSquareArrayWithRandomValues(sizeOfData); + Matrix matrix1 = new(numbersInMatrix1, sizeOfData, sizeOfData); + Matrix matrix2 = new(numbersInMatrix2, sizeOfData, sizeOfData); + timeByThreads.Add(TimeOfMultiThreadedMultiplying(matrix1, matrix2)); + timeSequential.Add(TimeOfSequentialMultiplying(matrix1, matrix2)); + } + double? mathematicalExpectationByThreads = timeByThreads.Average(); + double? mathematicalExpectationSequential = timeSequential.Average(); + double dispersionByThreads = DispersionCalculator(timeByThreads, mathematicalExpectationByThreads.Value); + double dispersionSequential = DispersionCalculator(timeSequential, mathematicalExpectationSequential.Value); + return new (double, double)[] { (mathematicalExpectationByThreads.Value, dispersionByThreads), (mathematicalExpectationSequential.Value, dispersionSequential) }; + } + + private static double DispersionCalculator(List time, double mathematicalExpectation) + { + var sumOfSquareDifference = Enumerable.Range(0, time.Count).Sum(i => (time[i] - mathematicalExpectation) * (time[i] - mathematicalExpectation)); + return sumOfSquareDifference / time.Count; + } + + private static int[,] CreateSquareArrayWithRandomValues(int size) + { + var random = new Random(); + var array = new int[size, size]; + for (int i = 0; i < size; i++) + { + for (int j = 0; j < size; j++) + { + array[i, j] = random.Next(); + } + } + return array; + } + + private static long TimeOfMultiThreadedMultiplying(Matrix matrix1, Matrix matrix2) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + Matrix.MultiThreadedMultiplyingMatrixes(matrix1, matrix2); + stopwatch.Stop(); + return stopwatch.ElapsedMilliseconds; + } + + private static long TimeOfSequentialMultiplying(Matrix matrix1, Matrix matrix2) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + Matrix.SequentialMultiplyingMatrixes(matrix1, matrix2); + stopwatch.Stop(); + return stopwatch.ElapsedMilliseconds; + } +} + diff --git a/Homework1/Homework1.csproj b/Homework1/Homework1.csproj new file mode 100644 index 0000000..d439800 --- /dev/null +++ b/Homework1/Homework1.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + + diff --git a/Homework1/Homework1.sln b/Homework1/Homework1.sln new file mode 100644 index 0000000..28f8e2a --- /dev/null +++ b/Homework1/Homework1.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 25.0.1706.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Homework1", "Homework1.csproj", "{14F8B15D-5607-477A-A472-19A069798644}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Homework1.Tests", "..\Homework1.Tests\Homework1.Tests.csproj", "{79329A91-4C97-4EBF-A9CA-034DF6F6322E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {14F8B15D-5607-477A-A472-19A069798644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14F8B15D-5607-477A-A472-19A069798644}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14F8B15D-5607-477A-A472-19A069798644}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14F8B15D-5607-477A-A472-19A069798644}.Release|Any CPU.Build.0 = Release|Any CPU + {79329A91-4C97-4EBF-A9CA-034DF6F6322E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79329A91-4C97-4EBF-A9CA-034DF6F6322E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79329A91-4C97-4EBF-A9CA-034DF6F6322E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79329A91-4C97-4EBF-A9CA-034DF6F6322E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {00E31478-F7C2-44B2-A7A6-52E93083F34F} + EndGlobalSection +EndGlobal diff --git a/Homework1/Matrix.cs b/Homework1/Matrix.cs new file mode 100644 index 0000000..31cb810 --- /dev/null +++ b/Homework1/Matrix.cs @@ -0,0 +1,180 @@ +namespace Homework1; + +/// +/// The matrix class implements methods: +/// matrix representation as a row, matrix multiplication by threads and sequential. +/// +public class Matrix +{ + public int[,] MatrixOfNumbers { get; private set; } + public int Height { get; private set; } = 0; + public int Width { get; private set; } = 0; + + /// + /// Creates new instance of Matrix class by file content. + /// + public Matrix(string filePath) + { + if (filePath == null) + { + throw new ArgumentNullException(nameof(filePath)); + } + string[] matrixInLines = File.ReadAllLines(filePath); + Height = matrixInLines.Length; + if (Height < 1) + { + throw new ArgumentException("There is no numbers in file"); + } + string[] parsedFirstLine = matrixInLines[0].Split(" ", StringSplitOptions.RemoveEmptyEntries); + Width = parsedFirstLine.Length; + if (Width < 1) + { + throw new ArgumentException("There is no numbers in file"); + } + MatrixOfNumbers = new int[Height, Width]; + for (int i = 0; i < Height; i++) + { + string line = matrixInLines[i]; + string[] stringOfNumbers = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (stringOfNumbers.Length != Width) + { + throw new ArgumentException("Amount of numbers in line isn't equal"); + } + if (i == 0) + { + Width = stringOfNumbers.Length; + } + for (int j = 0; j < Width; j++) + { + if (int.TryParse(stringOfNumbers[j], out int currentValue)) + { + MatrixOfNumbers[i, j] = currentValue; + } + else + { + throw new ArgumentException("Not inly numbers in file"); + } + } + } + } + + /// + /// Creates new instance of Matrix class. + /// + /// matrix in array + /// matrix height + /// matrix width + public Matrix(int[,] matrix, int height, int width) + { + MatrixOfNumbers = matrix; + Height = height; + Width = width; + } + + /// + /// return matrix in string + /// + public string PrintedMatrix() + { + string matrixInString = ""; + for (int i = 0; i < Height; i++) + { + for (int j = 0; j < Width; j++) + { + matrixInString += Convert.ToString(MatrixOfNumbers[i, j]) + ' '; + } + matrixInString += '\n'; + } + return matrixInString; + } + + /// + /// Multiplies matrixes sequential. + /// + public static Matrix SequentialMultiplyingMatrixes(Matrix matrix1, Matrix matrix2) + { + if (matrix1 == null) + { + throw new ArgumentNullException(nameof(matrix1)); + } + + if (matrix2 == null) + { + throw new ArgumentNullException(nameof(matrix2)); + } + + if (matrix1.Width != matrix2.Height) + { + throw new ArgumentException("Matrix sizes are inconsistent"); + } + var matrix3Numbers = new int[matrix1.Height, matrix2.Width]; + for (int i = 0; i < matrix1.Height; i++) + { + for (int j = 0; j < matrix2.Width; j++) + { + matrix3Numbers[i, j] = 0; + for (int k = 0; k < matrix1.Width; k++) + { + matrix3Numbers[i, j] += matrix1.MatrixOfNumbers[i, k] * matrix2.MatrixOfNumbers[k, j]; + } + } + } + + return new Matrix(matrix3Numbers, matrix1.Height, matrix2.Width); + } + + /// + /// Multiplies matrixes by threads. + /// + public static Matrix MultiThreadedMultiplyingMatrixes(Matrix matrix1, Matrix matrix2) + { + if (matrix1 == null) + { + throw new ArgumentNullException(nameof(matrix1)); + } + + if (matrix2 == null) + { + throw new ArgumentNullException(nameof(matrix2)); + } + + if (matrix1.Width != matrix2.Height) + { + throw new ArgumentException("Matrix sizes are inconsistent"); + } + var matrix3Numbers = new int[matrix1.Height, matrix2.Width]; + + int amountOfThreads = Environment.ProcessorCount; + var matrixMyltiplyingThreads = new Thread[amountOfThreads]; + + for (int i = 0; i < matrixMyltiplyingThreads.Length; i++) + { + int threadFirstLine = i; + uint amountOfElementsInMatrix = (uint)matrix1.Height * (uint)matrix2.Width; + matrixMyltiplyingThreads[i] = new Thread(() => + { + int currentLine = threadFirstLine; + while (currentLine < matrix1.Height) + { + for (int currentColumn = 0; currentColumn < matrix2.Width; ++currentColumn) + { + matrix3Numbers[currentLine, currentColumn] = Enumerable.Range(0, matrix1.Width).Sum(multiplyingIndex => matrix1.MatrixOfNumbers[currentLine, multiplyingIndex] * matrix2.MatrixOfNumbers[multiplyingIndex, currentColumn]); + } + currentLine += amountOfThreads; + } + }); + } + + foreach (var thread in matrixMyltiplyingThreads) + { + thread.Start(); + } + foreach (var thread in matrixMyltiplyingThreads) + { + thread.Join(); + } + return new Matrix(matrix3Numbers, matrix1.Height, matrix2.Width); + } +} + + diff --git a/Homework1/Program.cs b/Homework1/Program.cs new file mode 100644 index 0000000..cb37639 --- /dev/null +++ b/Homework1/Program.cs @@ -0,0 +1,41 @@ +using Homework1; + +using System.Text; + +if (args.Length != 2) +{ + Console.WriteLine("Expected two file pathes to matrixes for multiplication."); + return; +} + +string firstMatrixFilePath = args[0]; +string secondMatrixFilePath = args[1]; + +if (!File.Exists(firstMatrixFilePath)) +{ + throw new ArgumentException("First file path incorrect!"); +} + +if (!File.Exists(secondMatrixFilePath)) +{ + throw new ArgumentException("Second file path incorrect!"); +} + +Matrix matrix1 = new(firstMatrixFilePath); +Matrix matrix2 = new(secondMatrixFilePath); + +Console.Write("First matrix:\n" + matrix1.PrintedMatrix()); +Console.Write("Second matrix:\n" + matrix2.PrintedMatrix()); + +var matrix3 = Matrix.MultiThreadedMultiplyingMatrixes(matrix1, matrix2); +Console.Write("Result of multiplication:\n" + matrix3.PrintedMatrix()); +string matrix3FilePath = "..//..//..//ResultMatrix.txt"; + +FileStream fs = File.Create(matrix3FilePath); +if (!File.Exists(matrix3FilePath)) +{ + throw new ArgumentException("Файл не был создан"); +} +byte[] info = new UTF8Encoding(true).GetBytes(matrix3.PrintedMatrix()); +fs.Write(info, 0, info.Length); +fs.Close(); diff --git a/Homework1/results.txt b/Homework1/results.txt new file mode 100644 index 0000000..d954c32 --- /dev/null +++ b/Homework1/results.txt @@ -0,0 +1,20 @@ + Потоки: Последовательно: +Размер матрицы: 200*200 +Мат ожидание: 231 150 +Дисперсия: 1026 135 + +Размер матрицы: 400*400 +Мат ожидание: 1381 1261 +Дисперсия: 143678 118 + +Размер матрицы: 600*600 +Мат ожидание: 3908 4560 +Дисперсия: 1000470 106206 + +Размер матрицы: 800*800 +Мат ожидание: 8087 11800 +Дисперсия: 3552738 17762 + +Размер матрицы: 1000*1000 +Мат ожидание: 18090 23626 +Дисперсия: 30562572 1227916 \ No newline at end of file