[英]Make c# matrix code faster
在处理某些矩阵代码时,我担心性能问题。
这里是它的工作原理是:我有一个IMatrix
抽象类(所有矩阵操作等),通过实施ColumnMatrix
类。
abstract class IMatrix
{
public int Rows {get;set;}
public int Columns {get;set;}
public abstract float At(int row, int column);
}
class ColumnMatrix : IMatrix
{
private data[];
public override float At(int row, int column)
{
return data[row + columns * this.Rows];
}
}
在我的应用程序中经常使用该类,但是我担心性能问题。 测试仅对相同大小的锯齿状数组读取2000000x15矩阵,我得到1359ms的数组访问再次和9234ms的矩阵访问:
public void TestAccess()
{
int iterations = 10;
int rows = 2000000;
int columns = 15;
ColumnMatrix matrix = new ColumnMatrix(rows, columns);
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
matrix[i, j] = i + j;
float[][] equivalentArray = matrix.ToRowsArray();
TimeSpan totalMatrix = new TimeSpan(0);
TimeSpan totalArray = new TimeSpan(0);
float total = 0f;
for (int iteration = 0; iteration < iterations; iteration++)
{
total = 0f;
DateTime start = DateTime.Now;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
total = matrix.At(i, j);
totalMatrix += (DateTime.Now - start);
total += 1f; //Ensure total is read at least once.
total = total > 0 ? 0f : 0f;
start = DateTime.Now;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
total = equivalentArray[i][j];
totalArray += (DateTime.Now - start);
}
if (total < 0f)
logger.Info("Nothing here, just make sure we read total at least once.");
logger.InfoFormat("Average time for a {0}x{1} access, matrix : {2}ms", rows, columns, totalMatrix.TotalMilliseconds);
logger.InfoFormat("Average time for a {0}x{1} access, array : {2}ms", rows, columns, totalArray.TotalMilliseconds);
Assert.IsTrue(true);
}
所以我的问题是:我怎样才能使这件事更快? 有什么办法可以使我的ColumnMatrix.At更快? 干杯!
abstract class IMatrix
。 这是错误的,因为它不是接口,并且调用覆盖的方法比调用final(也就是非修饰符方法)要慢。 如果二维数组的性能要好得多,那么您不使用二维数组作为类的内部存储对象,而不是使用一维数组作为计算索引的开销吗?
您可以很容易地对已编写的数组代码进行优化,因为很明显,您正在按顺序访问内存。 这意味着JIT编译器在将其转换为本地代码方面可能会做得更好,并且可以带来更好的性能。
您没有考虑的另一件事是,内联仍然会碰到很多东西,因此如果未内联At方法(为什么不使用indexer属性,那么?)会由于调用的使用而遭受巨大的性能损失和堆栈操作。 最后,您应该考虑密封ColumnMatrix类,因为这将使JIT编译器的优化更加容易(调用肯定比callvirt更好)。
当您使用DateTime.Now
衡量性能时,结果是非常随机的。 时钟的分辨率约为1/20秒,因此您无需测量实际时间,而是测量时钟在时钟中滴答的位置。
您应该改用Stopwatch
类,它具有更高的分辨率。
对于元素的每次访问,您都要进行乘法运算:行+列* this.Rows。 您可能会看到内部是否也可以使用二维数组
您还将获得额外的开销,因为事情需要在类中抽象出来。 每次访问矩阵中的元素时,您都在进行额外的方法调用
更改为此:
interface IMatrix
{
int Rows {get;set;}
int Columns {get;set;}
float At(int row, int column);
}
class ColumnMatrix : IMatrix
{
private data[,];
public int Rows {get;set;}
public int Columns {get;set;}
public float At(int row, int column)
{
return data[row,column];
}
}
与抽象类相比,使用接口要好得多-如果需要通用类的功能,请为接口添加扩展方法。
同样,二维矩阵比锯齿形或扁平形的矩阵要快。
您可以使用并行编程来加快算法的速度。 您可以编译此代码,并比较常规矩阵方程(MultiplyMatricesSequential函数)和并行矩阵方程(MultiplyMatricesParallel函数)的性能。 您已经实现了此方法的性能比较功能(在“主要功能”中)。
您可以在Visual Studio 2010(.NET 4.0)下编译此代码
namespace MultiplyMatrices
{
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
#region Sequential_Loop
static void MultiplyMatricesSequential(double[,] matA, double[,] matB,
double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);
for (int i = 0; i < matARows; i++)
{
for (int j = 0; j < matBCols; j++)
{
for (int k = 0; k < matACols; k++)
{
result[i, j] += matA[i, k] * matB[k, j];
}
}
}
}
#endregion
#region Parallel_Loop
static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);
// A basic matrix multiplication.
// Parallelize the outer loop to partition the source array by rows.
Parallel.For(0, matARows, i =>
{
for (int j = 0; j < matBCols; j++)
{
// Use a temporary to improve parallel performance.
double temp = 0;
for (int k = 0; k < matACols; k++)
{
temp += matA[i, k] * matB[k, j];
}
result[i, j] = temp;
}
}); // Parallel.For
}
#endregion
#region Main
static void Main(string[] args)
{
// Set up matrices. Use small values to better view
// result matrix. Increase the counts to see greater
// speedup in the parallel loop vs. the sequential loop.
int colCount = 180;
int rowCount = 2000;
int colCount2 = 270;
double[,] m1 = InitializeMatrix(rowCount, colCount);
double[,] m2 = InitializeMatrix(colCount, colCount2);
double[,] result = new double[rowCount, colCount2];
// First do the sequential version.
Console.WriteLine("Executing sequential loop...");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
MultiplyMatricesSequential(m1, m2, result);
stopwatch.Stop();
Console.WriteLine("Sequential loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds);
// For the skeptics.
OfferToPrint(rowCount, colCount2, result);
// Reset timer and results matrix.
stopwatch.Reset();
result = new double[rowCount, colCount2];
// Do the parallel loop.
Console.WriteLine("Executing parallel loop...");
stopwatch.Start();
MultiplyMatricesParallel(m1, m2, result);
stopwatch.Stop();
Console.WriteLine("Parallel loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds);
OfferToPrint(rowCount, colCount2, result);
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
#endregion
#region Helper_Methods
static double[,] InitializeMatrix(int rows, int cols)
{
double[,] matrix = new double[rows, cols];
Random r = new Random();
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
matrix[i, j] = r.Next(100);
}
}
return matrix;
}
private static void OfferToPrint(int rowCount, int colCount, double[,] matrix)
{
Console.WriteLine("Computation complete. Print results? y/n");
char c = Console.ReadKey().KeyChar;
if (c == 'y' || c == 'Y')
{
Console.WindowWidth = 180;
Console.WriteLine();
for (int x = 0; x < rowCount; x++)
{
Console.WriteLine("ROW {0}: ", x);
for (int y = 0; y < colCount; y++)
{
Console.Write("{0:#.##} ", matrix[x, y]);
}
Console.WriteLine();
}
}
}
#endregion
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.