如何提高二维数组的性能?
How to improve the performance of a 2d array?
我正在尝试使用 Math.Net's dense matrix class。但是,它不支持 int
。所以,我必须为锯齿状的二维数组创建一个包装器。
我知道锯齿状数组有 better performances。
Data Size : 966 x 345
Naked 2d Array : 10 milliseconds
Naked Jagged Array : 6 milliseconds
Jagged Wrapper : 82 milliseconds
Dense Wrapper : 88 milliseconds
2d Wrapper : 62 milliseconds
根据我的测试结果,裸交错数组是最快的。
但是,就包装器而言,2d 包装器相对更快。
现在,我有两个问题:
- 为什么 jagged wrapper 比 2d wrapper 慢?
- 有没有可能让包装纸 运行 和裸包装一样快?
。
源代码
测试代码
Bitmap bmpImage = DataConverter2d.ReadGray("image.jpg");
int[][] intNakedJagged = DataConverter2d.ToInteger(bmpImage);
int[,] intNaked2d = JagMatrix<int>.To2d(intNakedJagged);
JagMatrix<int> intJaggedWrapper = new JagMatrix<int>(intNakedJagged);
DenMatrix<int> intDenWrapper = new DenMatrix<int>(intNaked2d);
Matrix<int> int2dWrapper = new Matrix<int>(intNaked2d);
Stopwatch sw1 = new Stopwatch();
sw1.Start();
double[,] dImage = DataConverter2d.ToDouble(intNaked2d);
sw1.Stop();
Console.WriteLine("Naked 2d Array : " + sw1.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw2 = new Stopwatch();
sw2.Start();
double[][] dImageJagged = DataConverter2d.ToDouble(intNakedJagged);
sw2.Stop();
Console.WriteLine("Naked Jagged Array : " + sw2.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw3 = new Stopwatch();
sw3.Start();
JagMatrix<double> dJagArray2d = DataConverter2d.ToDouble(intJaggedWrapper);
sw3.Stop();
Console.WriteLine("Jagged Wrapper : " + sw3.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw4 = new Stopwatch();
sw4.Start();
DenMatrix<double> dDenArray2d = DataConverter2d.ToDouble(intDenWrapper);
sw4.Stop();
Console.WriteLine("Dense Wrapper : " + sw4.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw5 = new Stopwatch();
sw5.Start();
Matrix<double> dArray2d = DataConverter2d.ToDouble(int2dWrapper);
sw5.Stop();
Console.WriteLine("2d Wrapper : " + sw5.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Console.ReadKey();
二维矩阵
public class Matrix<T> : IDisposable where T : struct , IComparable<T>
{
private T[,] __array2d;
public int Width { get; set; }
public int Height { get; set; }
public bool IsEmpty
{
get
{
if (__array2d == null) return true;
else return false;
}
}
public Matrix() { }
public Matrix(T[,] data)
{
this.Set(data);
}
public Matrix(int rows, int cols)
{
Width = rows;
Height = cols;
__array2d = new T[Width, Height];
}
public T Get(int x, int y)
{
if (__array2d == null)
{
throw new Exception("array is empty");
}
if (x < Width && y < Height)
{
if (__array2d != null)
{
return __array2d[x, y];
}
else
{
throw new Exception("array is null");
}
}
else
{
string message = string.Empty;
if (x >= Width) message = "x-value exceeds Width ";
if (y >= Height) message += "y-value exceeds Height ";
message += "in Array2d.Get(x,y).";
throw new Exception(message);
}
}
public void Set(int x, int y, T val)
{
if (__array2d == null)
{
__array2d = new T[Width, Height];
}
else
{
if (Width != __array2d.GetLength(0))
{
__array2d = null;
__array2d = new T[Width, Height];
}
}
if (x < Width && y < Height)
{
__array2d[x, y] = val;
}
else
{
throw new Exception(x + ", " + Width + "," + y + "," + Height);
}
}
public T this[int x, int y]
{
get
{
return Get(x, y);
}
set
{
Set(x, y, value);
}
}
public void Set(T[,] arr)
{
if (arr != null)
{
int rows = arr.GetLength(0);
int cols = arr.GetLength(1);
__array2d = arr;
Width = rows;
Height = cols;
}
else
{
throw new Exception("array is null");
}
}
#region IDisposable implementation
~Matrix()
{
this.Dispose(false);
}
protected bool Disposed { get; private set; }
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.Disposed)
{
if (disposing)
{
// Perform managed cleanup here.
//IDisposable disp = (IDisposable)_2dArray;
__array2d = null;
}
// Perform unmanaged cleanup here.
Width = 0;
Height = 0;
this.Disposed = true;
}
}
#endregion
}
2d 锯齿矩阵
public class JagMatrix<T> : IDisposable where T : struct , IComparable<T>
{
private T[][] __array2d;
public int Width { get; set; }
public int Height { get; set; }
public bool IsEmpty
{
get
{
if (__array2d == null) return true;
else return false;
}
}
public JagMatrix() { }
public JagMatrix(T[][] data)
{
this.Set(data);
}
public JagMatrix(int rows, int cols)
{
Width = rows;
Height = cols;
__array2d = new T[Width][];
for (int i = 0; i < Width; i++)
{
__array2d[i] = new T[Height];
}
}
public T Get(int x, int y)
{
if (__array2d == null)
{
throw new Exception("array is empty");
}
if (x < Width && y < Height)
{
if (__array2d != null)
{
return __array2d[x][y];
}
else
{
throw new Exception("array is null");
}
}
else
{
string message = string.Empty;
if (x >= Width) message = "x-value exceeds Width ";
if (y >= Height) message += "y-value exceeds Height ";
message += "in Array2d.Get(x,y).";
throw new Exception(message);
}
}
public void Set(int x, int y, T val)
{
if (__array2d == null)
{
__array2d = new T[Width][];
for (int i = 0; i < Width; i++)
{
__array2d[i] = new T[Height];
}
}
else
{
if (Width != __array2d.GetLength(0))
{
__array2d = null;
__array2d = new T[Width][];
for (int i = 0; i < Width; i++)
{
__array2d[i] = new T[Height];
}
}
}
if (x < Width && y < Height)
{
__array2d[x][y] = val;
}
else
{
throw new Exception(x + ", " + Width + "," + y + "," + Height);
}
}
public static T[,] To2d(T[][] source)
{
T[,] dest = new T[source.Length, source[0].Length];
for (int i = 0; i < source.Length; i++)
{
for (int j = 0; j < source[0].Length; j++)
{
dest[i,j] = source[i][j];
}
}
return dest;
}
public T this[int x, int y]
{
get
{
return Get(x, y);
}
set
{
Set(x, y, value);
}
}
public void Set(T[][] arr)
{
if (arr != null)
{
int rows = arr.Length;
int cols = arr[0].Length;
__array2d = arr;
Width = rows;
Height = cols;
}
else
{
throw new Exception("array is null");
}
}
#region IDisposable implementation
~JagMatrix()
{
this.Dispose(false);
}
protected bool Disposed { get; private set; }
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.Disposed)
{
if (disposing)
{
// Perform managed cleanup here.
//IDisposable disp = (IDisposable)_2dArray;
__array2d = null;
}
// Perform unmanaged cleanup here.
Width = 0;
Height = 0;
this.Disposed = true;
}
}
#endregion
}
二维密集矩阵
public class DenMatrix<T> : IDisposable where T : struct , IComparable<T>
{
private T[] __array1d;
public int Width { get; set; }
public int Height { get; set; }
public int Length { get { return Width * Height; } }
public bool IsEmpty
{
get
{
if (__array1d == null) return true;
else return false;
}
}
public DenMatrix() { }
public DenMatrix(T[,] data)
{
this.Set(data);
}
public DenMatrix(int rows, int cols)
{
Width = rows;
Height = cols;
__array1d = new T[Length];
}
public T Get(int x, int y)
{
if (__array1d == null)
{
throw new Exception("array is empty");
}
if (x < Width && y < Height)
{
if (__array1d != null)
{
return __array1d[x + y * Width];
}
else
{
throw new Exception("array is null");
}
}
else
{
string message = string.Empty;
if (x >= Width) message = "x-value exceeds Width ";
if (y >= Height) message += "y-value exceeds Height ";
message += "in Array2d.Get(x,y).";
throw new Exception(message);
}
}
public void Set(int x, int y, T val)
{
int length = Length;
if (__array1d == null)
{
__array1d = new T[length];
}
else
{
if (length != __array1d.Length)
{
__array1d = null;
__array1d = new T[length];
}
}
if (x < Width && y < Height)
{
__array1d[x + y * Width] = val;
}
else
{
throw new Exception(x + ", " + Width + "," + y + "," + Height);
}
}
public T[] To1d(T[,] array2d)
{
T[] array1d = new T[Length];
for (int x = 0; x < Height; x++)
{
for (int y = 0; y < Width; y++)
{
T val = array2d[x, y];
int index = x * Width + y;
array1d[index] = val;
}
}
return array1d;
}
public T this[int x, int y]
{
get
{
return Get(x, y);
}
set
{
Set(x, y, value);
}
}
public void Set(T[,] arr)
{
if (arr != null)
{
int rows = arr.GetLength(0);
int cols = arr.GetLength(1);
Width = cols;
Height = rows;
__array1d = To1d(arr);
}
else
{
throw new Exception("array is null");
}
}
#region IDisposable implementation
~DenMatrix()
{
this.Dispose(false);
}
protected bool Disposed { get; private set; }
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.Disposed)
{
if (disposing)
{
// Perform managed cleanup here.
//IDisposable disp = (IDisposable)_2dArray;
__array1d = null;
}
// Perform unmanaged cleanup here.
Width = 0;
Height = 0;
this.Disposed = true;
}
}
#endregion
}
double[][] ToDouble(int[][] image)
public static double[][] ToDouble(int[][] image)
{
int Width = image.Length;
int Height = image[0].Length;
double[][] array2d = new double[Width][];
for (int x = 0; x < Width; x++)
{
array2d[x] = new double[Height];
for (int y = 0; y < Height; y++)
{
double d = image[x][y] / 255.0;
array2d[x][y] = d;
}
}
return array2d;
}
DataConverter2d.Todouble(矩阵图像)
public static Matrix<double> ToDouble(Matrix<int> image)
{
int Width = image.Width;
int Height = image.Height;
Matrix<double> array2d = new Matrix<double>(Width, Height);
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
double d = image[x, y] / 255.0;
array2d[x, y] = d;
}
}
return array2d;
}
- Why is the jagged wrapper slower than 2d wrapper?
我无法使用 this 代码重新创建您的测试结果:
Data Size : 4000 x 4000
Naked 2d Array : 188 milliseconds
Naked Jagged Array : 202 milliseconds
Jagged Wrapper : 311 milliseconds
Dense Wrapper : 501 milliseconds
2d Wrapper : 343 milliseconds
- Is it possible to make the wrapper run as fast as the naked one?
让我们试试:
简化Get(x, y)
和Set(x, y, value)
方法并让数组自己检查边界:
public T this[int x, int y]
{
get
{
try {
return _array[x, y];
} catch (IndexOutOfRangeException e) {
throw new Exception(String.Format(
"index ({0}, {1}) exceeds size of Matrix ({2}, {3})",
x, y, Width, Height
));
}
}
set
{
try {
_array[x, y] = value;
} catch (IndexOutOfRangeException e) {
throw new Exception(String.Format(
"index ({0}, {1}) exceeds size of Matrix ({2}, {3})",
x, y, Width, Height
));
}
}
}
结果:
Data Size : 4000 x 4000
Naked 2d Array : 186 milliseconds
matrix (2d Wrapper): 308 milliseconds
FastMatrix : 246 milliseconds
使用 map 方法(在 Linq 中称为 Select
):
public FastMatrix<R> Map<R>(Func<T, R> func) where R : struct, IComparable<R>
{
FastMatrix<R> array2d = new FastMatrix<R>(Width, Height);
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
array2d._array[x, y] = func(_array[x, y]);
}
}
return array2d;
}
呼叫 map
:
FastMatrix<double> dFastMatrix2 = intFastMatrix.Map(v => (double)v / 255.0);
结果是:
Data Size : 4000 x 4000
Naked 2d Array : 186 milliseconds
matrix (2d Wrapper): 308 milliseconds
FastMatrix.Map : 184 milliseconds
这和裸二维数组一样快!
总结:
Data Size : 4000 x 4000
Naked 2d Array : 186 milliseconds
Naked Jagged Array : 200 milliseconds
Jagged Wrapper : 308 milliseconds
Dense Wrapper : 486 milliseconds
2d Wrapper : 308 milliseconds
Fast versions:
FastMatrix : 246 milliseconds
FastMatrix.Map : 184 milliseconds
完整代码为 here。
According to my test results, a naked jagged array is the fastest of all. But, in terms of wrapper, the 2d wrapper is relatively faster.
好吧,我的测试(运行 来自 的代码通过在发布模式下构建的 exe)显示不同的结果:
Data Size : 4000 x 4000
Naked 2d Array : 70 milliseconds
Naked Jagged Array : 67 milliseconds
Jagged Wrapper : 205 milliseconds
Dense Wrapper : 391 milliseconds
2d Wrapper : 227 milliseconds
Faster versions:
FastMatrix : 143 milliseconds
FastMatrix.Map : 130 milliseconds
我已经 运行 好几次了,两个 Jagged 实现都比它们的 2d 对应物快一点。 Dense 实现速度明显较慢。
Is it possible to make the wrapper run as fast as the naked one?
当然!您的 Get
/ Set
方法正在做很多不必要的工作,基本上以低效、冗余且不完全正确的方式重复 CLR 工作(例如,没有检查负索引)。不要那样做。 J。 Coenen 指出了正确的方向,但你可以走得更远。 C# 7.0 引入了一个名为 Ref locals and returns 的功能(关心性能的人们期待已久),它允许您创建具有与 CLR 数组相似性能特征的自定义 class 索引器。它甚至伴随着 use case/example for matrices :)
您只需将 class 索引器更改为:
矩阵
public ref T this[int x, int y] => ref __array2d[x, y];
JagMatrix
public ref T this[int x, int y] => ref __array2d[x][y];
DenMatrix
public ref T this[int x, int y] => ref __array1d[x + y * Width];
现在相同的测试显示不同的结果:
Data Size : 4000 x 4000
Naked 2d Array : 71 milliseconds
Naked Jagged Array : 68 milliseconds
Jagged Wrapper : 77 milliseconds
Dense Wrapper : 355 milliseconds
2d Wrapper : 79 milliseconds
Faster versions:
FastMatrix : 144 milliseconds
FastMatrix.Map : 132 milliseconds
如您所见,Jagged / 2d 包装器的性能与其 "naked" 对应物几乎相同。 Dense 实现仍然明显较慢(可能是由于除了 CRL 边界检查之外还手动计算索引),并且来自其他答案的 "faster versions" 位于中间。
我正在尝试使用 Math.Net's dense matrix class。但是,它不支持 int
。所以,我必须为锯齿状的二维数组创建一个包装器。
我知道锯齿状数组有 better performances。
Data Size : 966 x 345
Naked 2d Array : 10 milliseconds
Naked Jagged Array : 6 milliseconds
Jagged Wrapper : 82 milliseconds
Dense Wrapper : 88 milliseconds
2d Wrapper : 62 milliseconds
根据我的测试结果,裸交错数组是最快的。 但是,就包装器而言,2d 包装器相对更快。
现在,我有两个问题:
- 为什么 jagged wrapper 比 2d wrapper 慢?
- 有没有可能让包装纸 运行 和裸包装一样快?
。
源代码
测试代码
Bitmap bmpImage = DataConverter2d.ReadGray("image.jpg");
int[][] intNakedJagged = DataConverter2d.ToInteger(bmpImage);
int[,] intNaked2d = JagMatrix<int>.To2d(intNakedJagged);
JagMatrix<int> intJaggedWrapper = new JagMatrix<int>(intNakedJagged);
DenMatrix<int> intDenWrapper = new DenMatrix<int>(intNaked2d);
Matrix<int> int2dWrapper = new Matrix<int>(intNaked2d);
Stopwatch sw1 = new Stopwatch();
sw1.Start();
double[,] dImage = DataConverter2d.ToDouble(intNaked2d);
sw1.Stop();
Console.WriteLine("Naked 2d Array : " + sw1.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw2 = new Stopwatch();
sw2.Start();
double[][] dImageJagged = DataConverter2d.ToDouble(intNakedJagged);
sw2.Stop();
Console.WriteLine("Naked Jagged Array : " + sw2.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw3 = new Stopwatch();
sw3.Start();
JagMatrix<double> dJagArray2d = DataConverter2d.ToDouble(intJaggedWrapper);
sw3.Stop();
Console.WriteLine("Jagged Wrapper : " + sw3.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw4 = new Stopwatch();
sw4.Start();
DenMatrix<double> dDenArray2d = DataConverter2d.ToDouble(intDenWrapper);
sw4.Stop();
Console.WriteLine("Dense Wrapper : " + sw4.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Stopwatch sw5 = new Stopwatch();
sw5.Start();
Matrix<double> dArray2d = DataConverter2d.ToDouble(int2dWrapper);
sw5.Stop();
Console.WriteLine("2d Wrapper : " + sw5.ElapsedMilliseconds.ToString() + " milliseconds", "Elapsed time");
Console.ReadKey();
二维矩阵
public class Matrix<T> : IDisposable where T : struct , IComparable<T>
{
private T[,] __array2d;
public int Width { get; set; }
public int Height { get; set; }
public bool IsEmpty
{
get
{
if (__array2d == null) return true;
else return false;
}
}
public Matrix() { }
public Matrix(T[,] data)
{
this.Set(data);
}
public Matrix(int rows, int cols)
{
Width = rows;
Height = cols;
__array2d = new T[Width, Height];
}
public T Get(int x, int y)
{
if (__array2d == null)
{
throw new Exception("array is empty");
}
if (x < Width && y < Height)
{
if (__array2d != null)
{
return __array2d[x, y];
}
else
{
throw new Exception("array is null");
}
}
else
{
string message = string.Empty;
if (x >= Width) message = "x-value exceeds Width ";
if (y >= Height) message += "y-value exceeds Height ";
message += "in Array2d.Get(x,y).";
throw new Exception(message);
}
}
public void Set(int x, int y, T val)
{
if (__array2d == null)
{
__array2d = new T[Width, Height];
}
else
{
if (Width != __array2d.GetLength(0))
{
__array2d = null;
__array2d = new T[Width, Height];
}
}
if (x < Width && y < Height)
{
__array2d[x, y] = val;
}
else
{
throw new Exception(x + ", " + Width + "," + y + "," + Height);
}
}
public T this[int x, int y]
{
get
{
return Get(x, y);
}
set
{
Set(x, y, value);
}
}
public void Set(T[,] arr)
{
if (arr != null)
{
int rows = arr.GetLength(0);
int cols = arr.GetLength(1);
__array2d = arr;
Width = rows;
Height = cols;
}
else
{
throw new Exception("array is null");
}
}
#region IDisposable implementation
~Matrix()
{
this.Dispose(false);
}
protected bool Disposed { get; private set; }
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.Disposed)
{
if (disposing)
{
// Perform managed cleanup here.
//IDisposable disp = (IDisposable)_2dArray;
__array2d = null;
}
// Perform unmanaged cleanup here.
Width = 0;
Height = 0;
this.Disposed = true;
}
}
#endregion
}
2d 锯齿矩阵
public class JagMatrix<T> : IDisposable where T : struct , IComparable<T>
{
private T[][] __array2d;
public int Width { get; set; }
public int Height { get; set; }
public bool IsEmpty
{
get
{
if (__array2d == null) return true;
else return false;
}
}
public JagMatrix() { }
public JagMatrix(T[][] data)
{
this.Set(data);
}
public JagMatrix(int rows, int cols)
{
Width = rows;
Height = cols;
__array2d = new T[Width][];
for (int i = 0; i < Width; i++)
{
__array2d[i] = new T[Height];
}
}
public T Get(int x, int y)
{
if (__array2d == null)
{
throw new Exception("array is empty");
}
if (x < Width && y < Height)
{
if (__array2d != null)
{
return __array2d[x][y];
}
else
{
throw new Exception("array is null");
}
}
else
{
string message = string.Empty;
if (x >= Width) message = "x-value exceeds Width ";
if (y >= Height) message += "y-value exceeds Height ";
message += "in Array2d.Get(x,y).";
throw new Exception(message);
}
}
public void Set(int x, int y, T val)
{
if (__array2d == null)
{
__array2d = new T[Width][];
for (int i = 0; i < Width; i++)
{
__array2d[i] = new T[Height];
}
}
else
{
if (Width != __array2d.GetLength(0))
{
__array2d = null;
__array2d = new T[Width][];
for (int i = 0; i < Width; i++)
{
__array2d[i] = new T[Height];
}
}
}
if (x < Width && y < Height)
{
__array2d[x][y] = val;
}
else
{
throw new Exception(x + ", " + Width + "," + y + "," + Height);
}
}
public static T[,] To2d(T[][] source)
{
T[,] dest = new T[source.Length, source[0].Length];
for (int i = 0; i < source.Length; i++)
{
for (int j = 0; j < source[0].Length; j++)
{
dest[i,j] = source[i][j];
}
}
return dest;
}
public T this[int x, int y]
{
get
{
return Get(x, y);
}
set
{
Set(x, y, value);
}
}
public void Set(T[][] arr)
{
if (arr != null)
{
int rows = arr.Length;
int cols = arr[0].Length;
__array2d = arr;
Width = rows;
Height = cols;
}
else
{
throw new Exception("array is null");
}
}
#region IDisposable implementation
~JagMatrix()
{
this.Dispose(false);
}
protected bool Disposed { get; private set; }
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.Disposed)
{
if (disposing)
{
// Perform managed cleanup here.
//IDisposable disp = (IDisposable)_2dArray;
__array2d = null;
}
// Perform unmanaged cleanup here.
Width = 0;
Height = 0;
this.Disposed = true;
}
}
#endregion
}
二维密集矩阵
public class DenMatrix<T> : IDisposable where T : struct , IComparable<T>
{
private T[] __array1d;
public int Width { get; set; }
public int Height { get; set; }
public int Length { get { return Width * Height; } }
public bool IsEmpty
{
get
{
if (__array1d == null) return true;
else return false;
}
}
public DenMatrix() { }
public DenMatrix(T[,] data)
{
this.Set(data);
}
public DenMatrix(int rows, int cols)
{
Width = rows;
Height = cols;
__array1d = new T[Length];
}
public T Get(int x, int y)
{
if (__array1d == null)
{
throw new Exception("array is empty");
}
if (x < Width && y < Height)
{
if (__array1d != null)
{
return __array1d[x + y * Width];
}
else
{
throw new Exception("array is null");
}
}
else
{
string message = string.Empty;
if (x >= Width) message = "x-value exceeds Width ";
if (y >= Height) message += "y-value exceeds Height ";
message += "in Array2d.Get(x,y).";
throw new Exception(message);
}
}
public void Set(int x, int y, T val)
{
int length = Length;
if (__array1d == null)
{
__array1d = new T[length];
}
else
{
if (length != __array1d.Length)
{
__array1d = null;
__array1d = new T[length];
}
}
if (x < Width && y < Height)
{
__array1d[x + y * Width] = val;
}
else
{
throw new Exception(x + ", " + Width + "," + y + "," + Height);
}
}
public T[] To1d(T[,] array2d)
{
T[] array1d = new T[Length];
for (int x = 0; x < Height; x++)
{
for (int y = 0; y < Width; y++)
{
T val = array2d[x, y];
int index = x * Width + y;
array1d[index] = val;
}
}
return array1d;
}
public T this[int x, int y]
{
get
{
return Get(x, y);
}
set
{
Set(x, y, value);
}
}
public void Set(T[,] arr)
{
if (arr != null)
{
int rows = arr.GetLength(0);
int cols = arr.GetLength(1);
Width = cols;
Height = rows;
__array1d = To1d(arr);
}
else
{
throw new Exception("array is null");
}
}
#region IDisposable implementation
~DenMatrix()
{
this.Dispose(false);
}
protected bool Disposed { get; private set; }
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.Disposed)
{
if (disposing)
{
// Perform managed cleanup here.
//IDisposable disp = (IDisposable)_2dArray;
__array1d = null;
}
// Perform unmanaged cleanup here.
Width = 0;
Height = 0;
this.Disposed = true;
}
}
#endregion
}
double[][] ToDouble(int[][] image)
public static double[][] ToDouble(int[][] image)
{
int Width = image.Length;
int Height = image[0].Length;
double[][] array2d = new double[Width][];
for (int x = 0; x < Width; x++)
{
array2d[x] = new double[Height];
for (int y = 0; y < Height; y++)
{
double d = image[x][y] / 255.0;
array2d[x][y] = d;
}
}
return array2d;
}
DataConverter2d.Todouble(矩阵图像)
public static Matrix<double> ToDouble(Matrix<int> image)
{
int Width = image.Width;
int Height = image.Height;
Matrix<double> array2d = new Matrix<double>(Width, Height);
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
double d = image[x, y] / 255.0;
array2d[x, y] = d;
}
}
return array2d;
}
- Why is the jagged wrapper slower than 2d wrapper?
我无法使用 this 代码重新创建您的测试结果:
Data Size : 4000 x 4000
Naked 2d Array : 188 milliseconds
Naked Jagged Array : 202 milliseconds
Jagged Wrapper : 311 milliseconds
Dense Wrapper : 501 milliseconds
2d Wrapper : 343 milliseconds
- Is it possible to make the wrapper run as fast as the naked one?
让我们试试:
简化
Get(x, y)
和Set(x, y, value)
方法并让数组自己检查边界:public T this[int x, int y] { get { try { return _array[x, y]; } catch (IndexOutOfRangeException e) { throw new Exception(String.Format( "index ({0}, {1}) exceeds size of Matrix ({2}, {3})", x, y, Width, Height )); } } set { try { _array[x, y] = value; } catch (IndexOutOfRangeException e) { throw new Exception(String.Format( "index ({0}, {1}) exceeds size of Matrix ({2}, {3})", x, y, Width, Height )); } } }
结果:
Data Size : 4000 x 4000 Naked 2d Array : 186 milliseconds matrix (2d Wrapper): 308 milliseconds FastMatrix : 246 milliseconds
使用 map 方法(在 Linq 中称为
Select
):public FastMatrix<R> Map<R>(Func<T, R> func) where R : struct, IComparable<R> { FastMatrix<R> array2d = new FastMatrix<R>(Width, Height); for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { array2d._array[x, y] = func(_array[x, y]); } } return array2d; }
呼叫
map
:FastMatrix<double> dFastMatrix2 = intFastMatrix.Map(v => (double)v / 255.0);
结果是:
Data Size : 4000 x 4000 Naked 2d Array : 186 milliseconds matrix (2d Wrapper): 308 milliseconds FastMatrix.Map : 184 milliseconds
这和裸二维数组一样快!
总结:
Data Size : 4000 x 4000
Naked 2d Array : 186 milliseconds
Naked Jagged Array : 200 milliseconds
Jagged Wrapper : 308 milliseconds
Dense Wrapper : 486 milliseconds
2d Wrapper : 308 milliseconds
Fast versions:
FastMatrix : 246 milliseconds
FastMatrix.Map : 184 milliseconds
完整代码为 here。
According to my test results, a naked jagged array is the fastest of all. But, in terms of wrapper, the 2d wrapper is relatively faster.
好吧,我的测试(运行 来自
Data Size : 4000 x 4000
Naked 2d Array : 70 milliseconds
Naked Jagged Array : 67 milliseconds
Jagged Wrapper : 205 milliseconds
Dense Wrapper : 391 milliseconds
2d Wrapper : 227 milliseconds
Faster versions:
FastMatrix : 143 milliseconds
FastMatrix.Map : 130 milliseconds
我已经 运行 好几次了,两个 Jagged 实现都比它们的 2d 对应物快一点。 Dense 实现速度明显较慢。
Is it possible to make the wrapper run as fast as the naked one?
当然!您的 Get
/ Set
方法正在做很多不必要的工作,基本上以低效、冗余且不完全正确的方式重复 CLR 工作(例如,没有检查负索引)。不要那样做。 J。 Coenen 指出了正确的方向,但你可以走得更远。 C# 7.0 引入了一个名为 Ref locals and returns 的功能(关心性能的人们期待已久),它允许您创建具有与 CLR 数组相似性能特征的自定义 class 索引器。它甚至伴随着 use case/example for matrices :)
您只需将 class 索引器更改为:
矩阵
public ref T this[int x, int y] => ref __array2d[x, y];
JagMatrix
public ref T this[int x, int y] => ref __array2d[x][y];
DenMatrix
public ref T this[int x, int y] => ref __array1d[x + y * Width];
现在相同的测试显示不同的结果:
Data Size : 4000 x 4000
Naked 2d Array : 71 milliseconds
Naked Jagged Array : 68 milliseconds
Jagged Wrapper : 77 milliseconds
Dense Wrapper : 355 milliseconds
2d Wrapper : 79 milliseconds
Faster versions:
FastMatrix : 144 milliseconds
FastMatrix.Map : 132 milliseconds
如您所见,Jagged / 2d 包装器的性能与其 "naked" 对应物几乎相同。 Dense 实现仍然明显较慢(可能是由于除了 CRL 边界检查之外还手动计算索引),并且来自其他答案的 "faster versions" 位于中间。