2015-01-01 8 views
5

Sto lavorando a un emulatore come progetto side/fun, ma sto riscontrando alcuni problemi di prestazioni e non riesco a capire da dove provengano.Le prestazioni misteriose si degradano con il ciclo infinito Open GL +

L'applicazione è composta principalmente da un GLKView per la visualizzazione e da un thread separato con un loop infinito per l'emulazione della CPU. Ecco un esempio con tutto il codice di emulazione effettivo preso fuori che visualizza ancora il problema:

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    GLKView *glView = [[GLKView alloc] initWithFrame:self.view.bounds]; 
    glView.delegate = self; 
    glView.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 
    [EAGLContext setCurrentContext:glView.context]; 
    [self.view addSubview:glView]; 
    glView.enableSetNeedsDisplay = NO; 
    CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:glView selector:@selector(display)]; 
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    dispatch_get_main_queue(), ^{ 
     dispatch_async(dispatch_queue_create("yeah", DISPATCH_QUEUE_SERIAL), ^{ 
      CFTimeInterval lastTime = 0; 
      CFTimeInterval time = 0; 
      int instructions = 0; 
      while(1) { 
       // here be cpu emulation 
       if (lastTime == 0) { 
        lastTime = CACurrentMediaTime(); 
       } else { 
        CFTimeInterval newTime = CACurrentMediaTime(); 
        time += newTime - lastTime; 
        lastTime = newTime; 
       } 
       if (++instructions == 1000) { 
        printf("%f\n", 1/(time * 1000)); 
        time = 0; 
        instructions = 0; 
       } 
      } 
     }); 
} 
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 
{ 
    glClearColor(0.0, 0.0, 0.0, 1.0); 
    glClear(GL_COLOR_BUFFER_BIT); 
    // Here be graphics 
} 

@end 

In questo modo, i cicli infiniti è fondamentalmente solo contando E 'iterazioni e la stampa fuori di essa la frequenza in MHz.

Quindi, il problema è che quando l'app parte, il loop gira a circa 9-15 MHz (su un iPhone6), e se guardo il rapporto GPU in Debug Navigator di Xcode, posso vedere che il frame della CPU il tempo è 0.2ms poi, dopo l'esecuzione per un paio di secondi, il ciclo scende a 1-5 MHz, e le lasso di tempo della CPU aumenta a 0.6ms

Se io disattivare gli aggiornamenti GLKView, poi i loop mai diventare più lento

ho anche provato ad utilizzare diversi threading API (GDC, NSThread, pthread), ma che non sembra avere alcun impatto

La mia domanda è, sto facendo qualcosa di sbagliato da qualche parte? È solo un caso di GLKView che non viene inizializzato completamente per un paio di secondi e quindi utilizza meno CPU rispetto al normale e ottengo un aumento di velocità? In qualsiasi altro modo potrei strutturare il codice per ottenere il massimo delle prestazioni nel ciclo?

Aggiornamento ho fatto un po 'di test, e ho notato che il problema è presente anche quando si utilizza un CAEAGLLayer invece di un GLKView, inoltre, che non accade sul simulatore, solo su un dispositivo. Ho provato anche con un'applicazione OS X con NSOpenGLView e non succede neanche ...

Update 2 ho cercato di iniziare la discussione dopo un po ', invece di subito, e se il ritardo è più grande della tempo ci vuole di solito per il calo a verificarsi, il filo inizia già rallentato ... Non proprio sicuro di cosa fare di esso ...

metallo Aggiornamento ho provato ad utilizzare metallo invece di OpenGL, utilizzando semplici il modello di magazzino da Xcode, e sta accadendo anche con esso ...

+0

non si dovrebbe creare un ciclo infinito su qualsiasi thread . È necessario pianificare un'operazione che viene eseguita una volta per ogni fotogramma ed eseguirne l'attività una sola volta per ogni fotogramma. Ci sono molti modi per farlo, ma per prima cosa controlla se c'è qualcosa incorporato in glkit prima di usare qualcosa come link update update/delegate o nstimer. Possibili indicazioni: http://stackoverflow.com/questions/13653113/multithreading-glkview-drawing – LearnCocos2D

+1

Scusa, ma ho bisogno di un ciclo infinito, è così che funzionano gli emulatori. Eseguendo un'attività ogni frame mi darebbe al massimo 60 "operazioni" ogni secondo, ho bisogno di molto più di quello (circa 1000000 volte circa ...) Per gli aggiornamenti dello schermo va bene, ma non per l'emulazione della CPU –

+1

penso che sia necessario leggere molto di più su come funzionano l'emulazione, il multithreading e la sincronizzazione. Supponiamo che la CPU della macchina emulata venga eseguita a 1 mhz e che ci sia una coda di comandi, ognuno dei quali richiede un numero noto di cicli di CPU da eseguire. Dovresti quindi eseguire quelli, diciamo 1500, comandi che la cpu potrebbe eseguire in un dato intervallo di tempo ed emularli sulla macchina host. Quindi inizi un nuovo ciclo. Questo è un modello molto semplificato. Non avrai bisogno di un ciclo infinito e dovrai disporre di punti di sincronizzazione (tipicamente alla frequenza vsync della macchina emulata). – LearnCocos2D

risposta

2

La frequenza della CPU può essere ridotta dal sistema operativo per consumare meno energia/risparmiare batteria. Se il thread non utilizza molta potenza della CPU, il sistema operativo penserà che sia un buon momento per abbassare la frequenza. Su un computer desktop, d'altra parte, ci sono molti altri thread/processi in esecuzione (e le soglie sono probabilmente molto diverse), questo è probabilmente il motivo per cui sembra funzionare nel simulatore/in un'app desktop.

Ci sono diversi possibili motivi per cui il thread viene rilevato come non consumando molto tempo di CPU. Uno è che si chiama printf, e probabilmente c'è una sorta di blocco interno che rende il thread in attesa (forse anche CACurrentMediaTime). Un altro è probabilmente collegato agli aggiornamenti GLKView, anche se non sono sicuro di come.

+0

Mi sembra strano che possa strozzare un filo che non è assolutamente inattivo o sprecare cicli, ma facendo un lavoro reale. Ho pensato anche alla tua prima ipotesi, ma la mia app in realtà non ha né printf né CACurrentMediaTime (li ho aggiunti per il debug), e per quanto posso dire nessuna chiamata di sistema dato che è tutto un codice personalizzato. Continuo a pensare che potrebbe avere a che fare con OpenGL, dal momento che se disattivo gli aggiornamenti funziona bene ... –

+0

Hum si, sarebbe davvero strano. Ciò accade anche quando il codice OpenGL è solo chiaro, come nel tuo esempio? – Jerem

+0

Se il driver OpenGL sta anticipando il thread, è possibile visualizzare il profilo di Xcode. Sembra che contenga anche una funzionalità di diagnostica energetica, potrebbe essere anche utile. https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/AnalysingCPUUsageinYourOSXApp/AnalysingCPUUsageinYourOSXApp.html – Jerem

0

Quindi, non ho ancora capito perché sta accadendo, ma sono riuscito a trovare una soluzione alternativa, utilizzando un CALayer sostenuta da una CGBitmapContext invece di utilizzare OpenGL, ispirato https://github.com/lmmenge/MeSNEmu,

@interface GraphicLayer : CALayer 
{ 
    CGContextRef _context; 
} 
@end 

@implementation GraphicLayer 

-(id)init 
{ 
    self = [super init]; 
    if (self) { 
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
     _context = CGBitmapContextCreate(NULL, 418, 263, 8, 418 * 4, colorSpace, (CGBitmapInfo)kCGImageAlphaPremultipliedLast); 
     CFRelease(colorSpace); 
    } 
    return self; 
} 

- (void)display 
{ 
    CGImageRef CGImage = CGBitmapContextCreateImage(_context); 
    self.contents = (__bridge id)(CGImage); 
    CGImageRelease(CGImage); 
} 

@end 

@interface GraphicView : UIView 
@end 

@implementation GraphicView 

+ (Class)layerClass 
{ 
    return [GraphicLayer class]; 
} 

- (void)drawRect:(CGRect)rect 
{ 
} 

@end 

Usando questo i loop non rallentano (avendo un ciclo infinito o facendo un mucchio di operazioni su ogni frame), ma non sono del tutto sicuro del perché ...

+0

Hai eseguito il test su un altro tipo di iphone? Forse l'iPhone 6 ha una funzione speciale che riduce la velocità di clock della CPU quando la GPU sta eseguendo roba e il contesto bitmap non lo attiva. Questo è molto interessante, ma ho visto dispositivi in ​​cui l'attivazione di Wifi ha ridotto automaticamente la frequenza della GPU, ad esempio. – Jerem

Problemi correlati