1

ある座標系から別の座標系にポイントを再投影するメソッドのパフォーマンスを改善しようとしています。

List<Point> Reproject(List<Point> points, string sourceProjection, string destinationProjection)

座標変換を行うために、ポイントをサードパーティのライブラリ (FME) に渡します。私が現在達成しようとしているのは、ポイントの入力リストを取得し、そのリストから個別のポイントのみを選択し、それらのみを元のリストを再構築し、それをクライアントに返します。

私の基本的な計画は、 a を使用しDictionary<Point, int>てすべての個別のポイントを取得し、それらにインデックスを割り当ててから、インデックスを使用して元のリストを再構築することです。この動作をテストするために書いた大まかなコードを次に示します。

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;

私が現在実行している問題は、約 8M ポイントを超える場合の OutOfMemoryException です。これを行うためのより良い方法があるかどうか疑問に思っていますか?

4

2 に答える 2

1

このソリューションは、順序を維持し、一意のポイントのみを変換しながら、全体的なメモリ フットプリントを削減する可能性があります。

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;
}

しかし、全体として、おそらくまだかなりのメモリの塊です。可能であれば、メモリ使用量を減らすためにパフォーマンスを犠牲にすることができます (すべてのポイント、さらには複製を変換します)。いくつかの遅延処理を投入すると、必要に応じてポイントのみを変換して反復を停止するか、少なくともガベージ コレクターが使用されていない古いポイントを取得できるようにすることができます。

private IEnumerable<Point> Reproject(IEnumerable<Point> points, string sourceProjection, string destinationProjection)
{
    foreach(Point p in points)
        yield return FMETransform(p, sourceProjection, destinationProjection);
}
于 2013-07-11T03:28:15.030 に答える
1

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;

2.メモリの断片化を減らすには、distinctPoints に適切な初期サイズを設定することを検討してください (高速検索のために、ディクショナリである必要があります)。理想的なサイズは、points.Count よりも少し大きい素数です。(どれくらい大きいかを示唆する参考文献を見つけることができませんでした - たぶん 25%?)。

// You have to write "CalcDictionarySize". See above text.
int goodSize = CalcDictionarySize(points.Count);
var distinctPoints = new Dictionary<Point, int>(goodSize);

3.極端な場合、コードを実行する前に GC をリクエストします。(このアドバイスは議論の余地があるかもしれません。しかし、OOM を回避する他の方法を見つけることができなかったとき、私はそれを自分でうまく使用しました。)

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();

注意: 明示的に GC を呼び出すことは、簡単に行うことではありません。頻繁でもありません。「頻繁に」実行される GC は、オブジェクトを Gen 1 から Gen 2 にプッシュするだけで、FULL GC が実行されるまで収集されません。ユーザーが完了までに 5 秒以上かかる操作を要求し、OOM になりやすいことが示されている場合にのみ呼び出します。

于 2013-07-11T07:44:10.527 に答える