我有一个表示Grid
的锯齿状数组,每个数组项都是Cell
。 Grid
有2600行和2600列。我需要计算每个Cell
的坐标,创建Cell
对象的实例并将其添加到数组中。现在我正在使用的代码在我的计算机上占用3200-3800 ms
。有没有办法让它更快?
public GridCell[][] Cells;
private void CreateCells(int cellWidth, int cellHeight, int startX, int startY)
{
Cells = new GridCell[RowsCount][];
for (int i = 0; i < RowsCount; i++)
{
Cells[i] = new GridCell[ColumnsCount];
for (int j = 0; j < ColumnsCount; j++)
{
Point coordinate = new Point(startX + cellWidth * j, startY + cellHeight * i);
Cells[i][j] = new GridCell(cellWidth, cellHeight, coordinate);
}
}
}
答案 0 :(得分:3)
考虑到您正在使用6760000个对象,您可以获得良好的性能。主要的时间可能是在堆上构建新对象。因此,正如您所观察到的那样,使用结构而不是类可以为您提供支持。
如果你有大的CPU缓存,你也可以尝试使用单个数组,如:
public GridCell[] Cells = new GridCell[RowsCount * ColumnsCount];
使用寻址,例如:
Cells[i * ColumnsCount + j] = x;
答案 1 :(得分:2)
考虑使用Parallel.For - 这样的事情对多线程来说是微不足道的。
如图所示,如果您愿意更改功能(在这种情况下使用结构,但分配单个数组也可能有一些好处),可以在其他地方找到更大的初始增益。仍然可以使用线程来提高性能。 / p>
一些简单的测试:
//Single Threaded : 1701, 1825, 1495, 1606
//Multi Threaded : 1516, 1446, 1581, 1401
//Struct Single Threaded : 154, 157, 153, 151
//Struct MultiThreaded : 104, 107, 106, 103
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
while (true)
{
Benchmark("Single Threaded", () => CreateCells(1, 1, 0, 0));
Benchmark("Multi Threaded", () => CreateCellsThreaded(1, 1, 0, 0));
Benchmark("Struct Single Threaded", () => CreateStructCells(1, 1, 0, 0));
Benchmark("Struct MultiThreaded", () => CreateStructCellsThreaded(1, 1, 0, 0));
}
}
static void Benchmark(string Name, Action test)
{
var sw = Stopwatch.StartNew();
test();
UpdateResults(Name, sw.ElapsedMilliseconds.ToString());
GC.Collect();
}
static Dictionary<string, string> results = new Dictionary<string, string>();
static void UpdateResults(string key, string value)
{
value = value.PadLeft(4);
if (results.ContainsKey(key))
results[key] += ", " + value;
else
results[key] = value;
Console.Clear();
foreach (var kvp in results) Console.WriteLine(kvp.Key.PadRight(25) + ": " + kvp.Value);
}
const int RowsCount = 2600;
const int ColumnsCount = 2600;
public class Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
public class GridCell
{
public int width;
public int height;
public Point point;
public GridCell(int width, int height, Point point)
{
this.width = width;
this.height = height;
this.point = point;
}
}
public struct StructPoint
{
public int x;
public int y;
public StructPoint(int x, int y)
{
this.x = x;
this.y = y;
}
}
public struct StructGridCell
{
public int width;
public int height;
public StructPoint point;
public StructGridCell(int width, int height, StructPoint point)
{
this.width = width;
this.height = height;
this.point = point;
}
}
private static void CreateCells(int cellWidth, int cellHeight, int startX, int startY)
{
var Cells = new GridCell[RowsCount][];
for (int i = 0; i < RowsCount; i++)
{
Cells[i] = new GridCell[ColumnsCount];
for (int j = 0; j < ColumnsCount; j++)
{
Point coordinate = new Point(startX + cellWidth * j, startY + cellHeight * i);
Cells[i][j] = new GridCell(cellWidth, cellHeight, coordinate);
}
}
}
private static void CreateCellsThreaded(int cellWidth, int cellHeight, int startX, int startY)
{
var Cells = new GridCell[RowsCount][];
Parallel.For(0, RowsCount, new ParallelOptions { MaxDegreeOfParallelism = 4 }, i =>
{
Cells[i] = new GridCell[ColumnsCount];
for (int j = 0; j < ColumnsCount; j++)
{
Point coordinate = new Point(startX + cellWidth * j, startY + cellHeight * i);
Cells[i][j] = new GridCell(cellWidth, cellHeight, coordinate);
}
});
}
private static void CreateStructCells(int cellWidth, int cellHeight, int startX, int startY)
{
var Cells = new StructGridCell[RowsCount][];
for (int i = 0; i < RowsCount; i++)
{
Cells[i] = new StructGridCell[ColumnsCount];
for (int j = 0; j < ColumnsCount; j++)
{
var coordinate = new StructPoint(startX + cellWidth * j, startY + cellHeight * i);
Cells[i][j] = new StructGridCell(cellWidth, cellHeight, coordinate);
}
}
}
private static void CreateStructCellsThreaded(int cellWidth, int cellHeight, int startX, int startY)
{
var Cells = new StructGridCell[RowsCount][];
Parallel.For(0, RowsCount, i =>
{
Cells[i] = new StructGridCell[ColumnsCount];
for (int j = 0; j < ColumnsCount; j++)
{
var coordinate = new StructPoint(startX + cellWidth * j, startY + cellHeight * i);
Cells[i][j] = new StructGridCell(cellWidth, cellHeight, coordinate);
}
});
}
}
}
答案 2 :(得分:1)
您在评论中提到GridCell
和Point
都是类。类是迄今为止最常见的创建方法,但是如果您对不同的语义很好并且它们主要是保存数据而不是功能,那么您可以将它们转换为结构。
结构几乎就像一个类,但它是一个值类型而不是引用类型。这意味着代码如下:
Point a = new Point(0, 0);
Point b = a;
b.X = 5;
Console.WriteLine(a);
...如果它是一个结构,则会打印0
,如果它是一个类,则会打印5
。
这些语义允许将结构嵌入到其他内容中,而不必在堆上拥有自己的空间。从堆中分配可能很昂贵,因此如果您可以分配一个结构数组,则只需要进行一次分配而不是多次分配。