使 C# 矩阵代码更快
在处理一些矩阵代码时,我担心性能问题。
它的工作原理如下:我有一个 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 矩阵,数组访问时间为 1359 毫秒,矩阵访问时间为 9234 毫秒:
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 更快吗? 干杯!
Working on some matrix code, I'm concerned of performance issues.
here's how it works : I've a IMatrix
abstract class (with all matrices operations etc), implemented by a ColumnMatrix
class.
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];
}
}
This class is used a lot across my application, but I'm concerned with performance issues.
Testing only read for a 2000000x15 matrix against a jagged array of the same size, I get 1359ms for array access agains 9234ms for matrix access :
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);
}
So my question : how can I make this thing faster ? Is there any way I can make my ColumnMatrix.At faster ?
Cheers !
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
抽象类 IMatrix
。这是错误的,因为它不是接口,并且调用重写方法比调用final(也称为非修饰符方法)慢。abstract class IMatrix
. This is wrong because it's not interface and calling overridden methods is slower than calling final (aka non-modifier methods).您编写的数组代码可以很容易地进行优化,因为很明显您正在顺序访问内存。这意味着 JIT 编译器可能会更好地将其转换为本机代码,从而获得更好的性能。
您没有考虑的另一件事是,内联仍然会碰巧发生,所以如果您的 At 方法(为什么顺便说一下,不使用索引器属性?)没有内联,由于使用调用和堆栈操作,您将遭受巨大的性能损失。最后,您应该考虑密封 ColumnMatrix 类,因为这将使 JIT 编译器的优化变得更加容易(call 肯定比 callvirt 更好)。
The array code you've written can be optimized easily enough as it's clear that you're accessing memory sequentially. This means the JIT compiler will probably do a better job at converting it to native code and that will result in better performance.
Another thing you're not considering is that inlining is still hit and miss so if your At method (why not using an indexer property, by the way?) is not inlined you'll suffer a huge performance hit due to the use of call and stack manipulation. Finally you should consider sealing the ColumnMatrix class because that will make the optimization much easier for the JIT compiler (call is definitely better than callvirt).
如果二维数组的性能好得多,那么您不使用二维数组作为类的内部存储,而不是使用具有计算索引开销的一维数组吗?
If a two-dimensional array performs so much better, which don't you use a two-dimensional array for your class's internal storage, rather than the one-dimensional one with the overhead of calculating the index?
当您使用
DateTime.Now
来测量性能时,结果是相当随机的。时钟的分辨率约为 1/20 秒,因此您不是测量实际时间,而是测量代码中时钟滴答作响的位置。您应该使用
Stopwatch
类,它具有更高的分辨率。As you are using
DateTime.Now
to measure the performance, the result is quite random. The resolution of the clock is something like 1/20 second, so instead of measuring the actual time, you are measuring where in the code the clock happens to tick.You should use the
Stopwatch
class instead, which has much higher resolution.对于元素的每次访问,您都会执行乘法:行 + 列 * this.Rows。
您可能会看到,如果在内部也可以使用二维数组,
您还会获得额外的开销,因为该事物被抽象到类中。每次访问矩阵中的元素时,您都会执行额外的方法调用
For every access of an element you do a multiplication: row + columns * this.Rows.
You might see if internally you could also use a 2 dimensional array
You also gain extra overhead that the thing is abstracted away in a class. You are doing an extra method call everytime you access an element in the matrix
对此进行更改:
使用接口比抽象类更好 - 如果您需要它的通用功能,请为接口添加扩展方法。
此外,二维矩阵比锯齿状矩阵或扁平矩阵更快。
Change to this:
You're better off with the interface than the abstract class - if you need common functions of it add extension methods for the interface.
Also a 2D matrix is quicker than either the jagged one or your flattened one.
您可以使用并行编程来加速算法。
您可以编译此代码,并比较普通矩阵方程(MultiplyMatricesSequential 函数)和并行矩阵方程(MultiplyMatricesParallel 函数)的性能。您已经实现了该方法的性能比较函数(在 Main 函数中)。
您可以在 Visual Studio 2010 (.NET 4.0) 下编译此代码
You can use Parallel programming for speed up your algorithm.
You can compile this code, and compare the performance for normal matrix equations (MultiplyMatricesSequential function) and parallel matrix equations (MultiplyMatricesParallel function). You have implemented compare functions of performance of this methods (in Main function).
You can compile this code under Visual Studio 2010 (.NET 4.0)