2012-04-04 6 views
6

Tengo una aplicación de visor de mapas heredada que usa WinForms. Es sloooooow (La velocidad solía ser aceptable, pero Google Maps, Google Earth apareció y los usuarios se estropearon. Ahora estoy autorizado a hacerlo si es más rápido :)Descarga de transformaciones de coordenadas a la GPU

Después de hacer todas las mejoras de velocidad obvias (almacenamiento en caché, ejecución paralela, no dibujar lo que no necesita ser dibujado, etc.), mi profiler me muestra que el verdadero punto de asfixia son las transformaciones de coordenadas al convertir puntos del espacio-mapa al espacio-pantalla. Normalmente un código de conversión se ve así:

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

La implementación real es más complicado. Las latitudes/longitues se representan como enteros. Para evitar perder precisión, se multiplican por 2^20 (~ 1 millón). Así es como se representa una coordenada.

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

Es importante que los posibles factores de escala también están obligados explícitamente a la potencia de 2. Esto nos permite reemplazar la multiplicación con un BitShift. Así que el verdadero algoritmo es el siguiente:

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 representents la esquina superior izquierda de la pantalla en el espacio del mapa.) estoy pensando ahora en la descarga de este cálculo en la GPU. ¿Alguien puede mostrarme un ejemplo de cómo hacer eso?

Usamos .NET4.0, pero el código debería funcionar preferiblemente en Windows XP también. Además, las bibliotecas bajo GPL no podemos usar.

Respuesta

1

Ahora, un año después, el problema surgió nuevamente y encontramos una respuesta muy banal. Me siento un poco estúpido sin darme cuenta antes. Dibujamos los elementos geográficos en el mapa de bits a través de WinForms GDI ordinario. GDI es hardware acelerado. Todo lo que tenemos que hacer es NO hacer la transformación por nosotros mismos, pero establece los parámetros de escala del objeto System.Drawing.Graphics: Graphics.TranslateTransform (...) y Graphics.ScaleTransform (...) No hacemos incluso necesita el truco con el cambio de bit.

:)

2

le sugiero que busque en el uso de OpenCL y Cloo para hacer esto - echar un vistazo a la vector add example y luego cambiar esto para asignar los valores mediante el uso de dos ComputeBuffer s (uno para cada una de LatitudeInt y LongitudeInt en cada punto) a 2 salidas ComputeBuffer s. Sospecho que el código OpenCL haría es como la siguiente:

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

pero haría más de un coord transformada por núcleo. Tengo que irme corriendo, te recomiendo que leas en opencl antes de hacer esto.

Además, si el número de coords es razonable (< 100,000/1,000,000), la solución no basada en gpu probablemente sea más rápida.

1

Vengo de un fondo CUDA, y solo puedo hablar por las GPU NVIDIA, pero aquí va.

El problema al hacer esto en una GPU es su operación/tiempo de transferencia.

Tiene el orden de 1 operación para realizar por elemento. Realmente querrías hacer más de esto por elemento para obtener una mejora de velocidad real. El ancho de banda entre la memoria global y los hilos en una GPU es de alrededor de 100GB/s. Entonces, si tiene que cargar un entero de 4 bytes para hacer un FLOP, la velocidad máxima teórica es 100/4 = 25 FLOPS. Esto está lejos de los cientos de FLOPS anunciados.

Tenga en cuenta que este es el máximo teórico, el resultado real podría ser peor. Y esto es aún peor si está cargando más de un elemento. En su caso, se ve como 2, por lo que podría obtener un máximo de 12.5 FLOPS. En la práctica, es casi seguro que será más bajo.

Si esto te parece bien, ¡adelante!

+0

+1 para mostrar los límites teóricos. – user256890

+0

Solo para poner los números en perspectiva, ¿cuál es la velocidad aproximada de una CPU promedio de 2 núcleos en FLOPs? – user256890

+0

Depende de lo que llame un FLOP. Digamos que su CPU de 2 núcleos tiene una velocidad de reloj de 2 GHz, y un FLOP tarda 4 ciclos de reloj. Podría hacer 2 * 2/4 = 1 GFLOP. Esa es una estimación muy cruda. –

Cuestiones relacionadas