【问题标题】:Offloading coordinate transformations to GPU将坐标变换卸载到 GPU
【发布时间】:2012-04-18 10:04:23
【问题描述】:

我有一个使用 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 下的库。

【问题讨论】:

    标签: c# opencl gpu gpgpu coordinate-transformation


    【解决方案1】:

    我建议您考虑使用 OpenCL 和 Cloo 来执行此操作 - 看看 vector add example,然后使用两个 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。

    此外,如果坐标数合理(

    【讨论】:

      【解决方案2】:

      我来自 CUDA 背景,只能代表 NVIDIA GPU,但我是这样的。

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

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

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

      如果你觉得这听起来不错,那就去吧!

      【讨论】:

      • 只是把数字放在透视图中,平均 2 核 CPU 在 FLOP 中的大致速度是多少?
      • 这取决于你所说的翻牌圈。假设您的 2 核 CPU 的时钟速度为 2 GHz,而 FLOP 需要 4 个时钟周期。你可以做 2*2/4 = 1 GFLOP。这是一个非常粗略的估计。
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        :)

        【讨论】:

          猜你喜欢
          • 2019-01-02
          • 2011-02-09
          • 2012-10-13
          • 2015-05-31
          • 2022-11-04
          • 2021-11-20
          • 2018-09-06
          • 2020-01-09
          • 2016-09-16
          相关资源
          最近更新 更多