6

我有一个使用 WinForms 的旧版地图查看器应用程序。太慢了。(过去速度可以接受,但谷歌地图、谷歌地球出现了,用户被宠坏了。现在我可以让速度更快了:)

在进行了所有明显的速度改进(缓存、并行执行、不绘制不需要绘制的内容等)之后,我的分析器向我展示了真正的瓶颈是在将点从地图空间转换到屏幕空间时的坐标转换. 通常转换代码如下所示:

    public Point MapToScreen(PointF input)
    {
        // Note that North is negative!
        var result = new Point(
           (int)((input.X - this.currentView.X) * this.Scale),
           (int)((input.Y - this.currentView.Y) * this.Scale));
        return result;
    }

真正的实现更棘手。纬度/经度以整数表示。为避免丢失精度,它们乘以 2^20(约 100 万)。这就是坐标的表示方式。

public struct Position
{
    public const int PrecisionCompensationPower = 20;
    public const int PrecisionCompensationScale = 1048576; // 2^20
    public readonly int LatitudeInt; // North is negative!
    public readonly int LongitudeInt;
}

重要的是,可能的比例因子也明确地绑定到 2 的幂。这允许我们用位移替换乘法。所以真正的算法是这样的:

    public Point MapToScreen(Position input)
    {
        Point result = new Point();
        result.X = (input.LongitudeInt - this.UpperLeftPosition.LongitudeInt) >>
                     (Position.PrecisionCompensationPower - this.ZoomLevel);
        result.Y = (input.LatitudeInt - this.UpperLeftPosition.LatitudeInt) >> 
                     (Position.PrecisionCompensationPower - this.ZoomLevel);
        return result;
    }

(UpperLeftPosition 代表地图空间中屏幕的左上角。) 我现在正在考虑将这个计算卸载到 GPU上。谁能告诉我一个例子如何做到这一点?

我们使用 .NET4.0,但代码也最好在 Windows XP 上运行。此外,我们不能使用 GPL 下的库。

4

4 回答 4

2

我建议您考虑使用 OpenCL 和Cloo来执行此操作 - 看一下向量添加示例,然后将其更改为通过使用两个ComputeBuffers(每个点LatitudeIntLongitudeInt每个点一个)将值映射到 2 个输出ComputeBuffers。我怀疑 OpenCL 代码看起来像这样:

__kernel void CoordTrans(__global int *lat, 
                         __global int *lon, 
                         __constant int ulpLat,
                         __constant int ulpLon,
                         __constant int zl,
                         __global int *outx,
                         __global int *outy)
{
    int i = get_global_id(0);        
    const int pcp = 20;

    outx[i] = (lon[i] - ulpLon) >> (pcp - zl);
    outy[i] = (lat[i] - ulpLat) >> (pcp - zl);
}

但是你会为每个核心做不止一个坐标变换。我需要赶紧走,我建议你在这样做之前阅读 opencl。

此外,如果坐标数合理(<100,000/1,000,000),则基于非 gpu 的解决方案可能会更快。

于 2012-04-04T17:01:15.143 回答
1

我来自 CUDA 背景,只能代表 NVIDIA GPU,但这里有。

在 GPU 上执行此操作的问题是您的操作/传输时间。

您必须按 1 个操作的顺序执行每个元素。您真的希望对每个元素做更多的事情来获得真正的速度提升。全局内存和 GPU 上的线程之间的带宽约为 100GB/s。所以,如果你必须加载一个 4 字节的整数来做一个 FLOP,你的理论最大速度是 100/4 = 25 FLOPS。这与宣传的数百个 FLOPS 相去甚远。

请注意,这是理论上的最大值,实际结果可能更糟。如果您要加载多个元素,情况会更糟。在您的情况下,它看起来像 2,因此您可能会从中获得最多 12.5 FLOPS。在实践中,它几乎肯定会更低。

如果这对你来说听起来不错,那就去吧!

于 2012-04-05T01:35:01.093 回答
1

XNA 可用于执行您需要的所有转换,并提供非常好的性能。它也可以显示在 winforms 应用程序中:http://create.msdn.com/en-US/education/catalog/sample/winforms_series_1

于 2012-04-05T01:46:56.017 回答
1

现在一年后问题再次出现,我们找到了一个非常平庸的答案。我觉得有点愚蠢,没有早点意识到这一点。我们通过普通的 WinForms GDI 将地理元素绘制为位图。GDI 是硬件加速的。我们要做的不是自己做变换,而是设置 System.Drawing.Graphics 对象的缩放参数:Graphics.TranslateTransform(...) 和 Graphics.ScaleTransform(...) 我们甚至不需要这个技巧与位移。

:)

于 2013-06-14T08:27:40.410 回答