implementación multiprocess reutilizable en Sprite Kit

Estoy trabajando en un juego de Sprite Kit y necesito hacer un multithreading para mantener los fps sanos.

En la actualización, invoco una function para crear una gran cantidad de UIBezierPaths y combinarlos usando una biblioteca estática C ++.

Si tengo más de 10 forms, la velocidad de fotogtwigs disminuye drásticamente, así que decidí darle GCD y tratar de resolver el problema con un hilo por separado.

Puse esto en didMoveToView:

queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 

y en la function que se llama a cada cuadro, llamo a esto:

 dispatch_async(queue,^(void){[self heavyCalculationsFunc];}); 

Para alguien que conoce bien a GCD podría ser obvio que crea un nuevo hilo en cada fotogtwig, pero para mí aún no estaba claro.

Mi pregunta es, ¿hay alguna manera de reutilizar un hilo al que quiero llamar en la actualización?

Gracias por su ayuda de antemano!

Si tiene trabajo que debe hacer en cada fotogtwig, y ​​eso debe realizarse antes de que se genere el fotogtwig, es probable que el multihilo no lo ayude, a less que esté dispuesto a poner mucho esfuerzo en ello.

Mantener una velocidad de fotogtwigs es cuestión de time: no resources de CPU, solo time de panetworking. Para mantener una velocidad de fotogtwigs de 60fps, tiene 16,67 ms para hacer todo su trabajo. (En realidad, less que eso, porque SpriteKit y OpenGL necesitan parte de ese time para representar los resultados de su trabajo). Este es un problema síncrono: usted tiene trabajo, tiene una cantidad de time específica para hacerlo, por lo que el primer paso para mejorar el performance es hacer less trabajo o hacerlo de manera más eficiente.

La multiprocess, por otro lado, generalmente es para problemas asynchronouss: hay trabajo que debe hacer, pero no es necesario hacerlo en este momento , para que pueda continuar con las otras cosas que necesita hacer ahora (como regresar desde su método de actualización dentro de 16 ms para mantener el framerate arriba) y verifique los resultados de ese trabajo más tarde (por ejemplo, en un cuadro posterior).


Sin embargo, hay un poco de espacio entre estas dos definiciones: casi todos los dispositivos iOS modernos tienen CPU de varios núcleos, así que si juegas bien tus tarjetas puedes ajustar un poco de asincronía a tu problema sincrónico al paralelizar tu carga de trabajo. Hacer esto, y hacerlo bien, no es una pequeña hazaña: ha sido object de investigación e inversión por parte de grandes estudios de juegos durante años.

Eche un vistazo a la figura en "Cómo una escena procesa frameworks de animation" en la Guía de progtwigción de SpriteKit. Ese es tu reloj de 16 ms. Las regiones azul claro son segmentos de 16 ms de los que se encarga el código SpriteKit (y OpenGL, y otros frameworks) de Apple. Las otras rebanadas son tuyas. Desenrollamos ese diagtwig para un mejor aspecto:

SpriteKit render loop, linear

Si haces demasiado trabajo en cualquiera de esas secciones, o haces que la carga de trabajo de SpriteKit sea demasiado grande, todo se hace más grande que 16 ms y tu velocidad de fotogtwigs disminuye.

La oportunidad de subprocesamiento es trabajar un poco en la otra CPU durante la misma línea de time. Si el event handling las acciones, la física y las restricciones de SpriteKit no depende de ese trabajo, puede hacerlo en paralelo con esas cosas:

Código de juego en paralelo al código SK

O bien, si su trabajo tiene que suceder antes de que SpriteKit ejecute acciones y física, pero tiene otro trabajo que debe hacer en el método de update , puede enviar parte del trabajo a otro subprocess mientras realiza el rest de su trabajo de update . verifique los resultados mientras aún está en su método de update :

Código del juego en paralelo a sí mismo


Entonces, ¿cómo lograr estas cosas? Aquí hay un enfoque que usa grupos de despacho y la suposition de que las acciones / físicas / restricciones no dependen de su trabajo de background; está totalmente fuera de mi cabeza, por lo que puede no ser el mejor. 🙂

 // in setup dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t resultCatchingGroup = dispatch_group_create(); id stuffThatGetsMadeInTheBackground; - (void)update:(NSTimeInterval)currentTime { dispatch_group_async(group, queue, ^{ // Do the background work stuffThatGetsMadeInTheBackground = // ... }); // Do anything else you need to before actions/physics/constraints } - (void)didFinishUpdate { // wait for results from the background work dispatch_group_wait(resultCatchingGroup, DISPATCH_TIME_FOREVER); // use those results [self doSomethingWith:stuffThatGetsMadeInTheBackground]; } 

Por supuesto, dispatch_group_wait , como su nombre lo sugiere, bloqueará la ejecución para esperar hasta que se complete el trabajo en segundo plano, por lo que aún tendrá esa restricción de time de 16 ms. Si el primer plano funciona (el rest de su update , más las acciones / físicas / restricciones de SpriteKit funcionan y cualquier otro trabajo que se realice en respuesta a esas cosas) se realiza antes de que su trabajo de background funcione, lo estará esperando. Y si el background funciona más el renderizado de SpriteKit (más lo que sea que hagas en la update antes de generar el trabajo en segundo plano) tarda más de 16 ms, aún tendrás que soltar frameworks. Así que el truco para esto es conocer su carga de trabajo con suficiente detalle para progtwigrlo bien.

Considere un enfoque ligeramente diferente. Cree y mantenga su propia queue en lugar de get una queue de sistema.

a) llame a dispatch_queue_create para crear una nueva queue, guarde esta queue en su object. Use dispatch_async en esa queue para ejecutar su trabajo. Es posible que necesite sincronizar si tiene que completar antes del siguiente cuadro, etc.

b) Si tiene varios trabajos, considere una queue simultánea en lugar de una queue de serie, que puede o no hacer que las cosas sean más 'rápidas' dependiendo de sus dependencies.

Con GCD se supone que no debe pensar en hilos, si se crean / reutilizan hilos nuevos, etc. Solo piensen en las queues y en lo que les está presionando. También será de gran ayuda la lectura de la Guía de progtwigción de concurrency de Apple y la reference en gcd.