2012-06-27 16 views
10

Ho fatto un'app intensiva di calcolo utilizzando OpenCV per iOS. Certo che era lento. Ma era qualcosa come 200 volte più lento del mio prototipo di PC. Quindi lo stavo ottimizzando. Dai primi 15 secondi sono riuscito a ottenere una velocità di 0,4 secondi. Mi chiedo se ho trovato tutte le cose e quello che gli altri potrebbero voler condividere. Quello che ho fatto:Velocità massima da IOS/iPad/iPhone

  1. sostituito "double" tipi di dati all'interno OpenCV a "float". Double è a 64 bit e la CPU a 32 bit non può gestirli facilmente, quindi float mi ha dato un po 'di velocità. OpenCV usa il doppio molto spesso.

  2. Aggiunto "-mpfu=neon" alle opzioni del compilatore. L'effetto collaterale è stato un nuovo problema che il compilatore di emulazione non funziona più e tutto può essere testato solo su hardware nativo.

  3. Sostituito sin() e cos() implementazione con 90 tabelle di ricerca valori. La velocità era enorme! Questo è alquanto opposto al PC dove tali ottimizzazioni non danno alcun tipo di accelerazione. Il codice funzionava in gradi e questo valore è stato convertito in radianti per sin() e cos(). Anche questo codice è stato rimosso. Ma le tabelle di ricerca hanno fatto il lavoro.

  4. Abilitato "thumb optimizations". Alcuni post di blog consigliano esattamente l'opposto, ma questo perché il pollice rende le cose di solito più lente su armv6. armv7 è privo di problemi e rende le cose solo più veloci e più piccole.

  5. Per assicurarsi che le ottimizzazioni del pollice e -mfpu=neon funzionino al meglio e non introducano arresti anomali, ho rimosso completamente il target armv6. Tutto il mio codice è compilato allo armv7 e questo è indicato anche come requisito nell'app store. Questo significa che il minimo iPhone sarà 3GS. Penso che sia giusto abbandonare quelli più vecchi. In ogni caso quelli più vecchi hanno CPU più lente e l'app intensiva della CPU fornisce un'esperienza utente negativa se installata sul vecchio dispositivo.

  6. Naturalmente io uso -O3 flag

  7. ho cancellato "dead code" da OpenCV. Spesso quando ottimizzo OpenCV vedo un codice che chiaramente non è necessario per il mio progetto. Per esempio, spesso c'è un extra "if()" per verificare che la dimensione dei pixel sia 8 o 32 bit e so che ho bisogno solo di 8 bit. Ciò rimuove alcuni codici, offre all'ottimizzatore migliori possibilità di rimuovere qualcosa di più o di sostituirli con costanti. Anche il codice si adatta meglio alla cache.

Altri trucchi e idee? Per me abilitare il pollice e sostituire la trigonometria con le occhiate sono stati i produttori di boost e mi hanno fatto sorprendere. Forse sai qualcosa di più da fare che fa volare le app?

risposta

13

Se si stanno eseguendo molti calcoli in virgola mobile, sarebbe di grande utilità utilizzare il framework Accelerate di Apple. È progettato per utilizzare l'hardware in virgola mobile per eseguire calcoli su vettori in parallelo.

sarò anche affrontare i punti uno per uno:

1) Questo non è a causa della CPU, è perché, come del ARMv7-era sarà calcolato nel galleggiante unico a 32 bit operazioni in virgola mobile hardware del processore di punti (perché la mela ha sostituito l'hardware). Quelli a 64 bit verranno invece calcolati nel software. In cambio, le operazioni a 32 bit sono diventate molto più veloci.

2) NEON è il nome della nuova istruzione processore floating point impostato

3) Sì, questo è un metodo ben noto. Un'alternativa è usare il framework Apple che ho menzionato sopra. Fornisce funzioni sin e cos che calcolano 4 valori in parallelo. Gli algoritmi sono perfettamente sintonizzati in assembly e NEON in modo da fornire le massime prestazioni mentre si utilizza una batteria minima.

4) La nuova implementazione di pollice armv7 non ha gli svantaggi di armv6. Il consiglio di disattivazione si applica solo a v6.

5) Sì, considerando che l'80% degli utenti è su iOS 5.0 o superiore (i dispositivi armv6 hanno terminato il supporto in 4.2.1), che è perfettamente accettabile per la maggior parte delle situazioni.

6) Ciò si verifica automaticamente quando si attiva la modalità di rilascio.

7) Sì, questo non avrà tuttavia un effetto maggiore come i metodi precedenti.

Il mio consiglio è di fare il check-out Accelerare. In questo modo puoi essere certo di sfruttare tutta la potenza del processore in virgola mobile.

+0

Questo Accelerate era nuovo per me. È ancora un po 'difficile da usare in quanto ha bisogno di un livello di pensiero complessivo. Ma ancora possibile e magari andando a fare un tentativo. Lo contrassegno accettato più tardi perché voglio vedere se qui riceviamo suggerimenti più utili. –

+1

C'è una sessione nei video del WWDC 2012 che si occupa interamente del framework Accelerate. Dagli un'occhiata ^^ – borrrden

+0

http://adcdownload.apple.com//wwdc_2012/wwdc_2012_session_pdfs/session_708__the_accelerate_framework.pdf e https://developer.apple.com/videos/wwdc/2012/#708 sembra essere link per questo –

1

Fornisco un feedback ai post precedenti. Questo spiega alcune idee che ho provato a fornire sul codice morto al punto 7. Questo doveva essere un'idea leggermente più ampia. Ho bisogno di formattazione, quindi nessun modulo di commento può essere usato. Tale codice era in OpenCV:

for(kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++) { 
    vec[kk] = 0; 
} 

Volevo vedere come appare sul montaggio. Per assicurarsi che posso trovarlo in assemblea, ho avvolto in questo modo:

__asm__("#start"); 
for(kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++) { 
    vec[kk] = 0; 
} 
__asm__("#stop"); 

Ora premo "Prodotto -> generare output -> file Assembly" e quello che ottengo è:

@ InlineAsm Start 
    #start 
    @ InlineAsm End 
Ltmp1915: 
    ldr r0, [sp, #84] 
    movs r1, #0 
    ldr r0, [r0, #16] 
    ldr r0, [r0, #28] 
    cmp r0, #4 
    mov r0, r4 
    blo LBB14_71 
LBB14_70: 
Ltmp1916: 
    ldr r3, [sp, #84] 
    movs r2, #0 
Ltmp1917: 
    str r2, [r0], #4 
    adds r1, #1 
Ltmp1918: 
Ltmp1919: 
    ldr r2, [r3, #16] 
    ldr r2, [r2, #28] 
    lsrs r2, r2, #2 
    cmp r2, r1 
    bgt LBB14_70 
LBB14_71: 
Ltmp1920: 
    add.w r0, r4, #8 
    @ InlineAsm Start 
    #stop 
    @ InlineAsm End 

Un sacco di codice. I printf-D OUT valore (int)(descriptors->elem_size/sizeof(vec[0])) ed è stato sempre 64. Così ho hard-coded per essere 64 e superato di nuovo via assembler:

@ InlineAsm Start 
    #start 
    @ InlineAsm End 
Ltmp1915: 
    vldr.32 s16, LCPI14_7 
    mov r0, r4 
    movs r1, #0 
    mov.w r2, #256 
    blx _memset 
    @ InlineAsm Start 
    #stop 
    @ InlineAsm End 

Come si può vedere ora optimizer avuto l'idea e il codice è diventato molto più breve. È stato in grado di vettorializzare questo. Il punto è che il compilatore non sa sempre quali input sono costanti se questo è qualcosa come la dimensione della webcam o la profondità del pixel, ma in realtà nei miei contesti sono solitamente costanti e tutto quello che mi interessa è la velocità.

ho anche provato Accelera come suggerito di sostituire tre linee con:

__asm__("#start"); 
vDSP_vclr(vec,1,64); 
__asm__("#stop"); 

Assemblea ora sembra:

@ InlineAsm Start 
    #start 
    @ InlineAsm End 
Ltmp1917: 
    str r1, [r7, #-140] 
Ltmp1459: 
Ltmp1918: 
    movs r1, #1 
    movs r2, #64 
    blx _vDSP_vclr 
Ltmp1460: 
Ltmp1919: 
    add.w r0, r4, #8 
    @ InlineAsm Start 
    #stop 
    @ InlineAsm End 

sicuro se questo è più veloce di Bzero però. Nel mio contesto questa parte non richiede molto tempo e due varianti sembrano funzionare alla stessa velocità.

Un'altra cosa che ho imparato è usare la GPU. Maggiori informazioni qui http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework

Problemi correlati