在 C# 中快速访问矩阵作为锯齿状数组
Fast access to matrix as jagged array in C#
我创建了一个下三角距离矩阵(由于大小问题)作为锯齿状数组
注意:对象之间的距离是对称的
var dm = new double[size][]
for (var i = 0; i < size; i++)
{
dm[i] = new double[i+1];
for (var j = 0; j < i+1; j++)
{
dm[i][j] = distance(data[i], data[j]);
}
}
我需要经常访问这个矩阵,所以我为它做了下面的方法
private double GetValueOfDM(int row, int column, double[][] dm)
{
return column <= row ? distanceMatrix[row][column] : distanceMatrix[column][row];
}
通过 Visual Studio 性能分析,可以看出主要的速度问题在于 GetValueOfDM
方法的唯一行。
有人知道如何加快速度吗?
您可以删除方法中的条件并增加内存使用量以提高访问性能,如下所示:
var dm = new double[size][];
for (var i = 0; i < size; i++)
{
dm[i] = new double[size];
for (var j = 0; j < i+1; j++)
{
dm[i][j] = distance(data[i], data[j]);
dm[j][i] = dm[i][j];
}
}
private double GetValueOfDM(int row, int column, double[][] dm)
{
return dm[row][column];
}
现在您没有条件,编译器可以删除分支预测。此外,您应该 运行 测试您的实际用例,以确保它确实会成为一个问题。分析可能会表明分支条件将是代码中最慢的部分,但这并不一定意味着它实际上会显着减慢任何速度。此外,您可以尝试 运行在 Release 模式下使用它(使用编译器优化)以查看它如何影响性能。
如果您所处的系统没有足够的内存来使数组的大小翻倍,那么您的代码可能接近于访问锯齿状数组的最佳代码。
我猜你是在紧密循环中使用它?由于自动边界检查,.NET 中的数组并不/那么/快。如果您需要快速数组性能,请使用带缓冲区的指针:
sealed unsafe class DistanceData : IDisposable {
private Double* buffer;
private IntPtr bufferLength; // .NET uses IntPtr as a size_t equivalent.
private Int32 dim0Length;
public DistanceData(Int32 size, Double[] data) {
this.buffer = (Double*)Marshal.AllocHGlobal( size * size );
this.bufferLength = size * size;
this.dim0Length = size;
for(int y = 0; y < size; y++) {
for(int x = 0; x < y + 1; x++) {
this.buffer[ y * this.dim0Length + x ] = Distance( data[y], data[x] );
}
}
}
public void Dispose() {
Marshal.FreeHGlobal( this.buffer );
}
public Double GetValueOfDM(Int32 row, Int32 column) {
// WARNING: Without validation or your own bounds-checking, invalid values of `row` and `column` will cause access-violation errors and crash your program. Ensure that code that calls `GetValueOfDM` is correct and will never submit invalid values.
return this.buffer[ row * this.dim0Length + column];
}
}
您可以使用一维数组并像这样计算索引
i = (r * r + r) / 2 + c;
但是您仍然需要检查 r <= c 并进行翻转。
(r=行,c=列)
但这真的会更快吗?
我创建了一个下三角距离矩阵(由于大小问题)作为锯齿状数组 注意:对象之间的距离是对称的
var dm = new double[size][]
for (var i = 0; i < size; i++)
{
dm[i] = new double[i+1];
for (var j = 0; j < i+1; j++)
{
dm[i][j] = distance(data[i], data[j]);
}
}
我需要经常访问这个矩阵,所以我为它做了下面的方法
private double GetValueOfDM(int row, int column, double[][] dm)
{
return column <= row ? distanceMatrix[row][column] : distanceMatrix[column][row];
}
通过 Visual Studio 性能分析,可以看出主要的速度问题在于 GetValueOfDM
方法的唯一行。
有人知道如何加快速度吗?
您可以删除方法中的条件并增加内存使用量以提高访问性能,如下所示:
var dm = new double[size][];
for (var i = 0; i < size; i++)
{
dm[i] = new double[size];
for (var j = 0; j < i+1; j++)
{
dm[i][j] = distance(data[i], data[j]);
dm[j][i] = dm[i][j];
}
}
private double GetValueOfDM(int row, int column, double[][] dm)
{
return dm[row][column];
}
现在您没有条件,编译器可以删除分支预测。此外,您应该 运行 测试您的实际用例,以确保它确实会成为一个问题。分析可能会表明分支条件将是代码中最慢的部分,但这并不一定意味着它实际上会显着减慢任何速度。此外,您可以尝试 运行在 Release 模式下使用它(使用编译器优化)以查看它如何影响性能。
如果您所处的系统没有足够的内存来使数组的大小翻倍,那么您的代码可能接近于访问锯齿状数组的最佳代码。
我猜你是在紧密循环中使用它?由于自动边界检查,.NET 中的数组并不/那么/快。如果您需要快速数组性能,请使用带缓冲区的指针:
sealed unsafe class DistanceData : IDisposable {
private Double* buffer;
private IntPtr bufferLength; // .NET uses IntPtr as a size_t equivalent.
private Int32 dim0Length;
public DistanceData(Int32 size, Double[] data) {
this.buffer = (Double*)Marshal.AllocHGlobal( size * size );
this.bufferLength = size * size;
this.dim0Length = size;
for(int y = 0; y < size; y++) {
for(int x = 0; x < y + 1; x++) {
this.buffer[ y * this.dim0Length + x ] = Distance( data[y], data[x] );
}
}
}
public void Dispose() {
Marshal.FreeHGlobal( this.buffer );
}
public Double GetValueOfDM(Int32 row, Int32 column) {
// WARNING: Without validation or your own bounds-checking, invalid values of `row` and `column` will cause access-violation errors and crash your program. Ensure that code that calls `GetValueOfDM` is correct and will never submit invalid values.
return this.buffer[ row * this.dim0Length + column];
}
}
您可以使用一维数组并像这样计算索引
i = (r * r + r) / 2 + c;
但是您仍然需要检查 r <= c 并进行翻转。 (r=行,c=列)
但这真的会更快吗?