2012-04-04 16 views
6

Ho un'applicazione legacy map viewer che utilizza WinForms. È sloooooow. (La velocità era accettabile, ma Google Maps, Google Earth è venuto avanti e gli utenti si sono lasciati viziare. Ora sono autorizzato a effettuare se più veloce :)Offloading trasformazioni di coordinate in GPU

Dopo aver eseguito tutti gli evidenti miglioramenti di velocità (memorizzazione nella cache, esecuzione parallela, non disegno cosa non ha bisogno di essere disegnato, ecc.), il mio profilo mi mostra che il vero punto di soffocamento è la trasformazione delle coordinate quando si convertono i punti dallo spazio della mappa allo spazio dello schermo. Normalmente un codice di conversione è simile al seguente:

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

L'implementazione reale è più complicato. Latitudes/longitues sono rappresentati come numeri interi. Per evitare di perdere la precisione, vengono moltiplicati per 2^20 (~ 1 milione). Ecco come viene rappresentata una coordinata.

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

È importante che i possibili fattori di scala sono esplicitamente tenuti a potenza di 2. Ciò permette di sostituire la moltiplicazione con un Bitshift. Quindi la vera algoritmo simile a questo:

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 nell'angolo in alto a sinistra dello schermo nello spazio carta.) Sto pensando ora di scarico questo calcolo alla GPU. Qualcuno può mostrarmi un esempio su come farlo?

Utilizziamo .NET4.0, ma il codice dovrebbe preferibilmente funzionare anche su Windows XP. Inoltre, le librerie sotto GPL non possono essere utilizzate.

risposta

1

Ora un anno più tardi il problema è sorto di nuovo, e abbiamo trovato una risposta molto banale. Mi sento un po 'stupido non rendermene conto prima. Disegniamo gli elementi geografici in bitmap tramite il normale GDI di WinForms. GDI è accelerato hardware. Tutto quello che dobbiamo fare NON è fare da soli la trasformazione ma impostare i parametri di scala dell'oggetto System.Drawing.Graphics: Graphics.TranslateTransform (...) e Graphics.ScaleTransform (...) Noi non anche bisogno del trucco con il bit shifting.

:)

2

io suggerisco di guardare con OpenCL e Cloo per fare questo - dare un'occhiata al vector add example e poi cambiare questo per mappare i valori utilizzando due ComputeBuffer s (uno per ciascuna delle LatitudeInt e LongitudeInt in ogni punto) per 2 uscite ComputeBuffer s. Ho il sospetto che il codice OpenCL sarebbe simile a questa:

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

ma si dovrebbe fare più di una coord-transform per core. Ho bisogno di correre, ti consiglio di leggere su opencl prima di farlo.

Inoltre, se il numero di coords è ragionevole (< 100.000/1.000.000), la soluzione non basata su GPU sarà probabilmente più veloce.

1

Provengo da uno sfondo CUDA e posso parlare solo per le GPU NVIDIA, ma ecco qui.

Il problema con questa operazione su una GPU è il tempo di operazione/trasferimento.

Hai l'ordine di 1 operazione da eseguire per elemento. Volete davvero fare più di questo per elemento per ottenere un reale miglioramento della velocità. La larghezza di banda tra la memoria globale e i thread su una GPU è di circa 100 GB/s. Quindi, se devi caricare un intero 4 byte per fare un FLOP, la velocità massima teorica è 100/4 = 25 FLOPS. Questo è lontano dalle centinaia di FLOP pubblicizzati.

Nota questo è il massimo teorico, il risultato reale potrebbe essere peggiore. E questo è ancora peggio se stai caricando più di un elemento. Nel tuo caso, sembra 2, quindi potresti ottenere un massimo di 12.5 FLOP da esso. In pratica, sarà quasi certamente più basso.

Se questo ti suona bene, allora provaci!

+0

+1 per mostrare i limiti teorici. – user256890

+0

Solo per mettere i numeri in prospettiva, qual è la velocità approssimativa di una CPU media a 2 core in FLOP? – user256890

+0

Dipende da ciò che chiamate FLOP. Diciamo che la tua CPU a 2 core ha una velocità di clock di 2 GHz e un FLOP richiede 4 cicli di clock. Potresti fare 2 * 2/4 = 1 GFLOP. Questa è una stima molto approssimativa. –

Problemi correlati