提前很抱歉这篇长篇文章,感谢任何愿意花时间完成并给我反馈的人。
我有关于列表和数组操作的性能相关问题。
我编写了一个软件来对从一组传感器收集的数据执行一些操作。为了让它运行得更快,我目前正在尝试编写一些优化。
收集的数据位于 N×M 的双精度数组中(实际上实现为类扩展List<List<double>>
)。对于 i 的任何值,我们总是有this.Count() == N
和this[i].Count() == M
。基本上,它是一个矩形阵列。
该数组中的每个数据点都与 X-by-Y 地图上的一些点相关联。基本上,想象这是我从数据中制作的图像,以快速清晰的方式表示它。因此,对于每个数据点,都有一个List<int[]>
与之相关的地图点。这个事实由 表示List<int[]>[,] pointsLocal
。我还保存了一个静态文件List<int[]>[,]
来存储相同的信息:这样我可以pointsLocal
在一个细化循环中根据我的闲暇进行修改,并在下次调用这些方法时拥有一个新的副本。相同的传感器将始终与相同的点相关联,这就是我拥有该本地阵列的原因。有些点(实际上是大多数点)与多个传感器相关,因此出现在许多列表中。
在我的代码的其他部分,我能够正确识别出阵列的某些传感器存在问题,然后数据包含错误。这在 中表示private List<List<bool>> faultyData
。如果传感器给出了错误的输出,那么我必须假设与之相关的所有点都可能受到故障的影响,因此我不关心这些地图点中进一步分析的结果。
我的代码的计算部分为每个地图点聚合来自阵列中所有传感器的数据。我想要做的是预先确定一个我不需要执行任何分析的地图点子集。
该类PointsEqualityComparer
是 的自定义比较运算符int[]
,我使用它是因为地图点由它们的 2D 坐标标识。
public class Sinogram : List<List<double>>
{
//various enums
private List<List<bool>> faultyData; //faultyData[i][j] is true if there is an error in the data
//constructors
//some methods
public void dataAnalysis()
{
List<int[]>[,] pointsLocal = new List<int[]>[this.Count(), this[0].Count()];
List<int[]> faultyPoints = new List<int[]>();
//Fill pointsLocal with the correlated points from the static array
PointsEqualityComparer myComparer = new PointsEqualityComparer();
//Point selection parts (see later for the two different implementations)
//Actual analysis parts (not here because it is not relevant to my question, but it works)
}
}
比较器类如下。我已经发现该GetHashCode
方法必须返回尽可能独特的结果以提高性能,因此我按照您在此代码段中看到的那样实现了它。
public class PointsEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] p1, int[] p2)
{
bool result = (p1.Count() == p2.Count()) && (p1.Count() == 2) && (p1[0] == p2[0]) && (p1[1] == p2[1]);
return result;
}
public int GetHashCode(int[] obj)
{
return ((obj[0] + obj[1]) * (obj[0] + obj[1] + 1) + obj[1]);
}
}
现在是棘手的部分。对于我实际选择有趣的地图点的代码部分,我有两种不同的实现。有趣的意思是我必须在其上聚合来自传感器的数据的地图点。我通过实际识别容易出错的点并将它们从列表中删除来选择它们。
在我的第一个实现中,我遍历所有地图点列表。如果相应的传感器有故障,我会将这些点添加到故障点列表中(避免重复)。一旦我遍历所有点并生成错误点的完整列表,我会allPairsLocal
通过删除它们来更新。faultyPoints 的列表可能会变得相当大,特别是在某些情况下,当很多传感器报告错误时(最大理论大小超过 2000000 个元素,如果所有传感器都报告错误并且我正在尝试创建一个 1920*1080 的地图来绘制作为高清图像)
for (int i = 0; i <this.Count; i++)
{
for (int j = 0; j < this[i].Count; j++)
{
if (faultyData[i][j])
{
faultyPoints = faultyPoints.Union<int[]>(allPairsLocal[i, j], myComparer).ToList();
}
}
}
for (int i = 0; i <this.Count; i++)
{
for (int j = 0; j < this[i].Count; j++)
{
allPairsLocal[i, j] = allPairsLocal[i, j].Except(faultyPoints, myComparer).ToList();
}
}
在我的第二个实现中,我尝试使用较小的 faultyPoints 列表。因此,我所做的是,对于每个报告错误的传感器,使用它的列表从所有其他传感器(以及它自己的)中删除地图点。这使列表的维度更小,但代价是更多的 annidated 循环。
for (int i = 0; i <this.Count; i++)
{
for (int j = 0; j < this[i].Count; j++)
{
if (faultyData[i][j])
{
faultyPoints = allPairsLocal[i, j]. ToList();
for (int x = 0; x < this.Count; x++)
{
for (int y = 0; y < this[x].Count; y++)
{
allPairsLocal[x, y] = allPairsLocal[x, y].Except(faultyPoints, myComparer).ToList();
}
}
}
}
}
这两种实现都非常慢,我想这至少部分是因为数据集的大小。两者都比对整个数据集执行数据分析步骤花费更长的时间。有没有办法进行类似的操作但实现速度更快?有些步骤可能是平行的,但这并不会真正改变实质。是否有具有 O(1) 方法的数据结构来实现我在这里使用 Union 和 except 所做的事情?
再次感谢您阅读我的整篇文章。我很感激任何反馈,即使它不是一个完整的答案,我也很乐意澄清我能做什么。