C# 生成不同点的列表,但内存不足

C# 生成不同点的列表,但内存不足,c#,dictionary,out-of-memory,C#,Dictionary,Out Of Memory,我正在尝试改进一种方法的性能,这种方法可以将点从一个坐标系重新投影到另一个坐标系 List<Point> Reproject(List<Point> points, string sourceProjection, string destinationProjection) 列表重投影(列表点、字符串源投影、字符串目标投影) 为了进行坐标转换,我们将点传递到第三方库(FME)中。我目前试图实现的是获取点的输入列表,仅从该列表中选择不同的点,仅将这些点传递到转换引擎,然

我正在尝试改进一种方法的性能,这种方法可以将点从一个坐标系重新投影到另一个坐标系

List<Point> Reproject(List<Point> points, string sourceProjection, string destinationProjection)
列表重投影(列表点、字符串源投影、字符串目标投影)
为了进行坐标转换,我们将点传递到第三方库(FME)中。我目前试图实现的是获取点的输入列表,仅从该列表中选择不同的点,仅将这些点传递到转换引擎,然后重建原始列表并将其返回给客户机

我的基本计划是使用
字典
获取所有不同的点,并为它们分配一个索引,然后用索引重建原始列表。下面是我为测试这种行为而编写的一些粗略代码:

var distinctPoints = new Dictionary<Point, int>();
var distinctPointsMapping = new Dictionary<int, int>();
var pointNumber = 0;
var distinctPointNumber = 0;
foreach (var point in points)
{
    if (distinctPoints.ContainsKey(point))
    {
        distinctPointsMapping.Add(pointNumber, distinctPoints[point]);
    }
    else
    {
        distinctPoints.Add(point, distinctPointNumber);
        distinctPointsMapping.Add(pointNumber, distinctPointNumber);
        distinctPointNumber++;
    }

    pointNumber++;
}

Console.WriteLine("From an input of {0} points, I found {1} distinct points.", points.Count, distinctPointNumber);
var transformedPoints = new Point[distinctPointNumber]; // replace this with the call to the FME transformer

var returnVal = new List<Point>(points.Count);
pointNumber = 0;
foreach (var untransformedPoint in points)
{
    var transformedPoint = transformedPoints[distinctPointsMapping[pointNumber]];
    returnVal.Add(transformedPoint);
    pointNumber++;
}

return returnVal;
var distinctPoints=new Dictionary();
var distinctPointsMapping=新字典();
var pointNumber=0;
var distinctPointNumber=0;
foreach(变量点到点)
{
if(距离点容器(点))
{
添加(点编号,distinctPoints[point]);
}
其他的
{
distinctPoints.Add(点,distinctPointNumber);
添加(pointNumber,distinctPointNumber);
distinctPointNumber++;
}
pointNumber++;
}
WriteLine(“从{0}个点的输入中,我找到了{1}个不同点。”,points.Count,distinctPointNumber);
var transformedPoints=新点[distinctPointNumber];//将其替换为对FME变压器的调用
var returnVal=新列表(点数);
点数=0;
foreach(var未转换的点到点)
{
var transformedPoint=transformedPoints[distinctPointsMapping[pointNumber]];
returnVal.Add(transformedPoint);
pointNumber++;
}
返回值;

我目前遇到的问题是,当我的分数超过800万分时,会出现OutOfMemoryException。我想知道是否有更好的方法来实现这一点?

此解决方案可能会减少总体内存占用,同时保持秩序并仅转换唯一点:

List<Point> Reproject(List<Point> points, string sourceProjection, string destinationProjection)
{
    List<Point> returnPoints = new List<Point>(points.Count);

    var transformedPoints = new Dictionary<Point, Point>();

    foreach(var point in points)
    {
        Point projectedPoint;
        if (!transformedPoints.TryGetValue(point, out projectedPoint))
        {
            projectedPoint = FMETransform(point, sourceProjection, destinationProjection);
            transformedPoints.Add(point, projectedPoint);
        }
        returnPoints.Add(projectedPoint);
    }

    return returnPoints;
}
列表重投影(列表点、字符串源投影、字符串目标投影)
{
List returnPoints=新列表(points.Count);
var transformedPoints=新字典();
foreach(变量点到点)
{
点投射点;
如果(!transformedPoints.TryGetValue(点,out projectedPoint))
{
projectedPoint=FMETransform(点、源投影、目标投影);
转换点。添加(点,投影点);
}
returnPoints.Add(projectedPoint);
}
返回点;
}
但总的来说,可能还是一块很好的内存。如果可能,您可能会牺牲性能(转换所有点,甚至重复点),以减少内存使用。加入一些延迟处理,可能您只能在必要时转换点,然后停止迭代,或者至少让垃圾收集器在一些旧点未使用时拾取它们:

private IEnumerable<Point> Reproject(IEnumerable<Point> points, string sourceProjection, string destinationProjection)
{
    foreach(Point p in points)
        yield return FMETransform(p, sourceProjection, destinationProjection);
}
私有IEnumerable重投影(IEnumerable点、字符串源投影、字符串目标投影)
{
foreach(点中的点p)
收益率回报率FMETransform(p,sourceProjection,destinationProjection);
}

1.词典占用大量内存。自动调整大型词典的大小特别容易出现问题(内存碎片=>OOM,这远远早于您的预期)

替换:

var distinctPointsMapping = new Dictionary<int, int>();
...
distinctPointsMapping.Add(pointNumber, distinctPoints[point]);
...
distinctPointsMapping.Add(pointNumber, distinctPointNumber);
var distinctPointsMapping = new List<Int>(points.Count);
...
distinctPointsMapping[pointNumber] = distinctPoints[point];
...
distinctPointsMapping[pointNumber] = distinctPointNumber;
然后在方法开始时:

GarbageCollect_Major();

警告:显式调用GC不是一件容易的事情。也不经常。“过于频繁”完成的GC可能只是将对象从Gen 1推到Gen 2,在完成完整GC之前,不会收集这些对象。我只在用户请求一个需要5秒以上才能完成的操作时才调用它,而且已经证明该操作很容易出现OOM。

老实说,我会这样做。我不太明白为什么有映射和
int
索引指针。如果将例程更改为:
returnpoints.Distinct().Select(p=>FMETransform(p,sourceProjection,destinationProjection).ToList()
?(我不确定“调用FME转换器”是什么样的,所以我只把
FMETransform
)编辑:至少这可能有助于减少数百万长的重复数组的数量。根据方法签名,我必须以正确的顺序向客户端返回他们给我的点数,因此不幸的是,这并不像将输入减少到一组不同的点数并进行转换那样简单。非常感谢这一点wer.我按照你的建议用一个列表替换了字典,然后我想我还是全力以赴用一个int[]替换它更有用。查看字典构造函数,不需要CalcDictionarySize()方法,因为构造函数将根据非素数参数找到下一个最大的素数。这两个增强功能让我在内存再次耗尽之前再拟合3M点,这非常好!@Coxy很高兴听到。如果它满足您的需要,请将其“标记”为答案。单击“空心复选标记”在它的左边,它会变为实心。:)事实上,它只让你走得更远一点。所以,也许还没有把它标记为答案…哦,酷,你标记了它。我已经做了很多编辑,还有次要的回答。我想这是我第一次实际接受的答案
public void GarbageCollect_Major()
{
    // Force GC of two generations - to get any recent unneeded objects up to their finalizers.
    GC.Collect(1, GCCollectionMode.Forced);

    GC.WaitForPendingFinalizers();

    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

    // This may be dubious. But it seemed to maintain more responsive system.
    // (perhaps 5-20 ms) Because full GC stalls .Net, give time to threads (related to GUI?)
    System.Threading.Thread.Sleep(10);
}
GarbageCollect_Major();